Вы находитесь на странице: 1из 1263

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

для WEB
Библиотека профессионала
WEB
PROGRAMMING

MARTY HALL
LARRY BROWN

Prentk» Hall PTR, Upper Saddle River, N J 07458 Sun Microsystems Press
www.phptr.com A Prentice Hall Title
ПРОГРАММИРОВАНИЕ
для WEB
Б и б л и о т е к а профессионала

МАРТИ ХОЛЛ
ЛЭРРИ БРАУН

Москва • Санкт-Петербург • Киев


2002
ББК 32.973.26-018.2.75
Х72
УДК 681.3.07

Издательский дом "Вильяме"


Зав. редакцией С.Н. Тригуб

Перевод с английского и редакция В.В. Вейшмана

По общим вопросам обращайтесь в Издательский дом "Вильяме"


по адресу: info@williamspublishing.com, http://www.williamspublishing.com

Холл, Марти, Браун, Лэрри.


Х72 Программирование для Web. Библиотека профессионала.: Пер. с англ. —
М.: Издательский дом "Вильяме", 2002. — 1264 с.: ил. — Парал. тит. англ.
ISBN 5-8459-0237-1 (рус.)
В данной книге читатель найдет все необходимые сведения, позволяющие
создавать Web-страницы, включать в них исполняемый код, а также реализовать
программы, выполняющиеся на стороне сервера. В ней достаточно полно описа­
ны языковые конструкции, соответствующие спецификации HTML 4.0, приведе­
ны подробные сведения о языке Java, рассматривается создание сервлетов nJSP,
обработка XML-документов^ построение программ, выцолняющихся на стороне
клиента (аплетов иJavaScript-cцeнapиeв), и многие другие вопросы.

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

Authoiized ti-anslation fiom the English language edition published by Prentice Hall PTR.
Copyiight © Sun Microsystem, Inc., 2001. All rights reseiTed. No pait of this book may be reproduced or
ti^ansmitted in any foiin or by any means, electionic or mechanical, including photocopying, recording or by any
infonnation storage retiieval system, without peiTnission from the Publisher.
Russian language edition published by Williams Publishing House according to the Agieement with
RM Enterprises International, Copyiight © 2002

ISBN 5-8459-0237-1 (рус.) © Издательский дом "Вильяме", 2001


ISBN 0-13-089793-0 (англ.) © Sun Microsystem, Inc., 2001
Оглавление

Оглавление

Введение 25

ЧАСТЬ I. ЯЗЫК HTML 33


Глава 1. Создание web-страниц с помощью HTML 4.0 34
Глава 2. Элементы блокового уровня HTML 4.0 54
Глава 3. Элементы текстового уровня HTML 4.0 82
Глава 4. Фреймы 110
Глава 5. Каскадные листы стилей 136

ЧАСТЬ П . П Р О Г Р А М М И Р О В А Н И Е Н А Я З Ы К Е JAVA 177


Глава 6. Общие сведения о языке Java 178
Глава 7. Объектно-ориентированное программирование на Java 206
Глава 8. Синтаксис Java 252
Глава 9. Аплеты и основные действия с графикой 310
Глава 10. Java 2d; графика в Java 2 358
Глава 11. События, связанные с мышью и клавиатурой 394
Глава 12. Диспетчеры компоновки 418
Глава 13. Компоненты awt 454
Глава 14. Основы Swing 542
Глава 15. Расширенные средства Swing 602
Глава 16. Использование потоков 664
Глава 17. Сетевое программирование 718

Ч А С Т Ь III. П Р О Г Р А М М Ы , В Ы П О Л Н Я Ю Щ И Е С Я
Н А СТОРОНЕ СЕРВЕРА 779
Глава 18. HTML-формы 780
Глава 19. Java на стороне сервера: сервлеты 816
Глава 20. JavaServer Pages 900
Глава 21. Аплеты как интерфейс к программам на стороне сервера 990
Глава 22. JDBC 1016
Глава 23. Обработка XML-документов 1050

Ч А С Т Ь rv^, J A V A S C R I P T 1099
Глава 24. JavaScript: динамически изменяющиеся Web-страницы 1100
Глава 25. Краткое руководство по JavaScript 1172
Предметный указатель 1254
в Содержание

Содержание

Введение 25

ЧАСТЬ 1. ЯЗЫК HTML 33


Глава 1. Создание web-страниц с помощью HTML 4.0 34
1.1. HyperText Markup Language 35
1.2. HTML 4.0 и другие стандарты HTML 37
1.3. Порядок публикации документа в Web 38
Создание документа 38
Размещение документа в Web 39
Проверка документа 41
1.4. Структура HTML-документа 41
Шаблон HTML-документа 42
Декларация DOCTYPE 43
1.5. Элемент HEAD — высокоуровневая информация.., 45
Обязательный элемент в составе HEAD 45
Необязательные элементы в составе HEAD 46
1.6. BODY -- основная часть документа 50
Глава 2. Элементы блокового уровня HTML 4.0 54
2.1. Заголовки 56
2.2. Основные элементы... 58
Основной абзац 58
Абзацы с сохранением пробелов 59
Цитирование с отступом 60
Адрес 61
2.3. Нумерованные, маркированные списки... 61
Нумерованные списки 61
Маркированные списки 64
Списки определений 65
2.4. Таблицы 66
Структура таблицы 66
Определение строк таблицы 71
Ячейки заголовков и ячейки данных 73
Группировка содержимого таблицы 75
2.5. Формы 78
2.6. Прочие элементы блокового уровня 78
Глава 3. Элементы текстового уровня HTML 4.0 82
3.1. Физические стили отображения символов 83
3.2. Логические стили отображения символов 88
3.3. Построение гипертекстовых ссылок 90
3.4. Встроенные изображения 94
Содержание

Анимационные GIF-файлы 94
Элемент IMG 94
3.5. Карты изображений на стороне клиента 98
3.6. Включение объектов в документ 101
Встроенные аплеты 101
Встроенные аудио- и видеофрагменты, а также объекты
в других форматах 104
Управляющие элементы ActiveX 104
Бегущая строка 106
3.7. Управление переводом строк 108
Глава 4. Фреймы 110
4.1. Шаблон документа с фреймами 112
4.2. Разбиение окна броузера на фреймы 113
4.3. Определение содержимого фреймов 118
Примеры 120
4.4. Определение фрейма ддя отображения документа 122
Зарезервированные имена фреймов 124
4.5. Разрешение типичных проблем... 125
Создание закладок для фреймов 125
Вывод фреймов на печать 126
Одновременное обновление нескольких фреймов 126
Запрет вывода документа в составе фрейма 129
Создание пустого фрейма 130
4.6. Встроенные фреймы 130
Глава 5. Каскадные листы стилей 136
5.1. Правила стилей 138
5.2. Внешние и локальные листы стилей 140
Внешние листы стилей 140
Элемент STYLE и листы стилей JavaScript 142
Встроенные правила стилей 142
5.3. Селекторы 143
HTML-элементы 143
Классы, определяемые пользователем 144
Идентификаторы, определяемые пользователем 145
Псевдоклассы якоря 146
5.4. Предшествование правил 146
5.5. Свойства шрифтов 147
5.6. Свойства для определения фона и переднего плана 153
5.7. Текстовые свойства 156
5.8. Свойства блоков с обрамлением 159
Границы 160
Обрамление 160
Заполненная область 162
Типы отображения блока 163
5.9. Изображения и плавающие элементы 163
5.10. Свойства списков 165
8 Содержание

5.11. Стандартные единицы для определения значений свойств 166


Единицы длины 166
Цвет 167
5.12. Слои 167
Определение слоев с помощью элементов LAYER и ILAYER 168
Работа со слоями посредством листов стилей 172
5.13. Резюме 176

Ч А С Т Ь П . П Р О Г Р А М М И Р О В А Н И Е Н А Я З Ы К Е JAVA 177
Глава 6. О б щ и е с в е д е н и я о я з ы к е Java 178
6.1. Возможности Java 180
Средства сетевого взаимодействия и работа в Web 180
Кроссплатформенная поддержка Java 183
П р о с т о т а Java 186
Объектно-ориентированные средства Java 186
Стандартные библиотеки Java 187
6.2. Java: м и ф ы и реальность 188
Java предназначен исключительно для Web 189
Кроссплатформенная поддержка Java 189
Простота Java 190
Java — объектно-ориентированный язык (для тех,
кто придерживается соответствующего подхода) 191
Java — язык для разработки различных типов программ 191
6.3. Версии Java 192
Какой версии отдать предпочтение 193
О с н о в н ы е сведения о Java 194
6.4. Начинаем работу 194
Установка Java 195
Инсталляция броузера с поддержкой Java 196
Доступ к документации по Java API 196
Установка интегрированного пакета разработки
(необязательное действие) 197
Создание и запуск Java-программы 198
6.5. Несколько простых Java-программ 198
П р и л о ж е н и е Hello, World 199
П а р а м е т р ы командной строки 199
Аплет Hello, World 200
Параметры, передаваемые аплету 202
Глава 7. О б ъ е к т н о - о р и е н т и р о в а н н о е п р о г р а м м и р о в а н и е на Java 206
7.1. П е р е м е н н ы е экземпляра 207
7.2. Методы 210
7.3. Конструкторы и ссылка this 211
Статические инициализационные блоки 214
7.4. Деструкторы 214
7.5. Переопределение 215
7.6. Подготовка классов к повторному использованию 218
Содержание 9

7.7.Javadoc 223
Дескрипторы javadoc 224
Параметры командной строки javadoc 227
7.8. Наследование 230
7.9. Интерфейсы и абстрактные классы 233
7.10. Пакеты, CLASSPATH и JAR-архивы 242
Переменная окружения CLASSPATH 245
7.11. Модификаторы 247
Модификаторы области видимости 248
Прочие модификаторы 249
Глава 8. Синтаксис Java 252
8.1. Синтаксические правила 253
8.2. Простые типы 255
Преобразование простых типов 257
8.3. Операторы, условные операторы и циклы 258
Арифметические операторы 258
Условные операторы 259
Циклы 265
8.4. Класс Math 268
Константы 268
Методы общего назначения 268
- Тригонометрические методы 270
Biglnteger и BigDecimal 270
8.5. Ввод и вывод 271
Вывод в стандартный выходной поток 272
Вывод в стандартный поток ошибок 273
Чтение из стандартного входного потока 274
8.6. Выполнение программ, отличных от Java 274
8.7. Ссылки 280
Соглашения по передаче параметров 282
Оператор instanceof 283
8.8. Работа со строками 284
Методы класса String 285
Конструкторы 290
8.9. Массивы 291
Создание массива в два этапа 292
Создание массива в один этап 292
Многомерные массивы 294
8.10. Векторы 295
Конструкторы 295
Методы 295
8.11. Пример создания простого бинарного дерева 297
8.12. Исключения 302
Обработка исключения 302
Использование нескольких блоков catch 304
Выражение finally 305
10 Содержание

Использование ключевого слова throws и явная генерация


исключений 306
Необрабатываемые исключения 307
Глава 9. Аплеты и основные действия с графикой 310
9.1. Что такое аплеты 311
9.2. Создание аплета 312
Шаблон аплета 312
Шаблон HTML-файла 313
9.3. Пример аплета 314
Автоматическое обновление информации в окне аплета 316
Повторная загрузка аплета в процессе разработки 316
Отображение данных, выведенных в стандартный выходной поток 317
9.4. Жизненный цикл аплета 318
9.5. Прочие методы аплета 319
9.6. Элемент APPLET 325
9.7. Чтение параметров 327
Пример чтения параметров аплетом 328
9.8. HTML-элемент OBJECT 330
9.9. JaVa Plug-In 332
9.10. Графические приложения 334
9.11. Графические операции 335
Операции рисования 336
Работа с цветом и со шрифтом 339
Режимы рисования 339
Координаты и области отсечения 340
9.12. Вывод изображений 340
Загрузка аплетом изображений, заданных с помощью
относительного URL 341
Загрузка аплетом изображений, заданных
с помощью абсолютного URL 343
Загрузка изображений приложениями 345
9.13. Предварительная загрузка изображений 347
9. J4. Управление загрузкой изображений... 351
9.15. Резюме 357
Глава 10. Java 2d: графика в Java 2 358
10.1. Общие сведения о Java 2D 360
Методы класса Graphics2D 362
10.2. Отображение форм 365
Классы Shape 366
10.3. Стили рисования 370
Классы рисования 370
Заполнение контура изображением 373
10.4. Рисование прозрачных форм 376
10.5. Использование локальных шрифтов 378
10.6. Стили пера 381
Атрибуты пера 381
Содержание 11

10.7. Преобразование координат 386


Н е л и н е й н о е масштабирование 389
10.8. П р о ч и е возможности Java 2D 390
Глава 11. С о б ы т и я , с в я з а н н ы е с м ы ш ь ю и клавиатурой 394
11.1. Поддержка событий... 396
Отображение кругов 397
11.2. Поддержка событий путем... 399
11.3. Обработка событий с помощью... 400
11.4. Обработка событий с помощью... 402
11.5. Стандартные обработчики событий 403
11.6. Низкоуровневая обработка событий 409
11.7. Поле редактирования с проверкой о р ф о г р а ф и и 412
11.8. "Чертежная доска" 414
М о д и ф и ц и р о в а н н ы й вариант "чертежной доски" 416
11.9. Резюме 417
Глава 12. Д и с п е т ч е р ы к о м п о н о в к и 418
12. 1. Д и с п е т ч е р компоновки FlowLayout 420
Конструкторы FlowLayout 421
Методы FlowLayout 421
12.2. Д и с п е т ч е р компоновки BorderLayout 422
Конструкторы BorderLayout 424
Методы BorderLayout 424
12.3. Д и с п е т ч е р компоновки GridLayout 425
Конструкторы GridLayout 425
Методы GridLayout 427
12.4. Д и с п е т ч е р компоновки CardLayout 427
Конструкторы CardLayout 431
Методы CardLayout 431
12.5. Д и с п е т ч е р компоновки GridBagLayout 432
Объект GridBagConstraints 433
П р и м е р использования GridBagLayout 434
Конструктор GridBagLayout 438
Методы GridBagLayout 438
12.6. Д и с п е т ч е р компоновки BoxLayout 439
Конструктор BoxLayout 442
Методы BoxLayout 442
12.7. Размещение компонентов вручную 443
12.8. Э ф ф е к т и в н о е использование диспетчеров компоновки 444
Использование вложенных контейнеров 445
Отключение диспетчеров компоновки для некоторых контейнеров 448
Резервирование пустого пространства вокруг компонентов 449
Глава 13. К о м п о н е н т ы awt 454
13.1. Класс Canvas 456
Создание и использование объектов Canvas 457
П р и м е р компонента, созданного с помощью Canvas 457
12 Содержание

13.2. Класс C o m p o n e n t 460


13.3. "Легковесные" компоненты в Java 1.1 466
13.4. Класс Panel 469
Диспетчер компоновки по умолчанию: FlowLayout 469
Создание и использование панелей 470
П р и м е р использования Panel для группировки компонентов 470
13.5. Класс Container 472
13.6. Класс Applet 474
13.7. Класс ScrollPane 474
Создание и использование ScrollPane 474
П р и м е р объекта ScrollPane, включающего панель,
которая содержит 100 кнопок 474
13.8. Класс Frame 475
Диспетчер компоновки по умолчанию: BorderLayout 476
Создание и использование объектов Frame 476
Примеры использования объектов Frame 477
Закрытие окон Frame 479
Меню 480
Методы класса Frame 481
13.9. Сериализация окон 482
Запись окна на диск 483
Чтение окна с диска 483
П р и м е р окна Frame, допускающего сохранение на диске 484
13.10. Класс Dialog 486
Создание и использование окна Dialog 487
П р и м е р диалогового окна: подтверждение завершения программы 487
13.11. Класс FileDialog 489
П р и м е р отобрг1жения файлов с помощью компонента TextAiea 489
13.12. Класс Window 491
Диспетчер компоновки по умолчанию: BorderLayout 491
Создание и использование объекта Window 491
13.13. Обработка событий управляющих элементов 492
Децентрализованная обработка событий 493
Централизованная обработка событий 495
13.14. Класс Button 496
Конструкторы 496
П р и м е р аплета с тремя кнопками 497
Методы класса Button 498
Поддержка действий с кнопками 498
13.15. Класс Checkbox 501
Конструкторы 501
П р и м е р создания флгьжков опций 502
Методы Checkbox 503
Поддержка событий Checkbox 503
13.16. Переключатели опций 504
Конструкторы 504
П р и м е р использования флажков и переключателей опций 505
Содержание 13

Методы классов CheckboxGroup и Checkbox 506


Поддержка событий CheckboxGroup 506
13.17. Элемент Choice 507
Конструктор 507
П р и м е р раскрывающегося списка 507
Методы класса Choice 508
Поддержка событий, связанных с объектом Choice 509
13.18. Элемент List 510
Конструкторы 511
П р и м е р ы элементов List 511
Методы класса List 512
Поддержка событий элемента List 514
13.19. Класс TextField 518
Конструкторы 519
П р и м е р создания полей редактирования 519
Методы TextField 520
Методы класса TextComponent 520
Обработка событий, связанных с элементом TextField 523
13.20. Класс TextArea 523
Конструкторы 523
П р и м е р ы текстовых областей 524
Методы классаTextAiea 525
Поддержка событий TextArea 526
13.21. Класс Label 526
Конструкторы 526
П р и м е р использования объектов Label 527
Методы класса Label 527
Поддержка событий Label 528
13.22. Полосы прокрутки и л и н е й н ы е регуляторы 530
Конструкторы 530
П р и м е р ы линейных регуляторов 531
Методы Scrollbar 532
Поддержка событий Scrollbar 534
13.23. Контекстные меню 535
Конструкторы 536
П р и м е р использования раскрывающегося меню 536
Методы класса P o p u p M e n u 538
Поддержка событий, связанных с раскрывающимися меню 539
Глава 14. О с н о в ы Swing 542
14.1. Общие сведения о Swing 544
Различия между Swing и AWT 544
14.2. Компонент JApplet 552
14.3. Компонент J F r a m e 553
14.4. Компонент JLabel 555
Новые средства: изображения, рамки и HTML-содержимое 555
Конструкторы JLabel 556
14 Содержание

Методы JLabel 557


14.5. Компонент JButton 559
Н о в ы е возможности: пиктограммы, выравнивание
и "горячие клавиши" 559
HTML-код для создания надписей на кнопках 560
Конструкторы JButton 561
Методы классов J B u t t o n и AbstractButton 561
14.6. Компонент JPanel 563
Конструкторы JPanel 564
Новая возможность: обрамление 564
Методы класса BorderFactory 564
14.7. Компонент JSlider 567
Н о в ы е возможности: разметка шкалы 568
Конструкторы JSlider 568
Методы класса JSlider 569
14.8. Компонент JColorChooser 571
Конструкторы 572
Методы класса JColorChooser 572
14.9. Внутренние окна 575
Конструктор J I n t e r n a l F r a m e 575
Методы класса JInternalFrame 575
14.10. К о м п о н е н т J O p t i o n P a n e 578
Методы класса J O p t i o n P a n e 579
14.11. Компонент JToolBar 583
Конструкторы JToolBar 584
Методы JToolBar 585
14.12. Компонент JEditorPane 589
Переход по гипертекстовым ссылкам 590
Конструкторы JEditorPane 590
Методы J E d i t o r P a n e 591
Реализация простого Web-броузера 592
Поддержка H T M L и JavaHelp 595
14.13. П р о с т ы е Swing-компоненты 595
Компонент JCheckBox 595
Компонент JRadioButton 597
Компонент JTextField 598
Компонент JTextArea 598
Компонент JFileChooser 598
Глава 15. Р а с ш и р е н н ы е с р е д с т в а Swing 602
15.1. Использование произвольных моделей данных
и средств просмотра 604
15.2. Компонент JList 604
JList как ф и к с и р о в а н н ы й набор пунктов 605
Компонент JList с изменяемым набором пунктов 609
Использование JList с произвольной моделью данных 612
JList и средства визуализации, определяемые разработчиком 617
Содержание 15

15.3.JTree 621
Простой объект J T r e e 621
Методы класса J T r e e 625
Обработка событий J T r e e 625
15.4.JTable 634
Простой способ создания JTable 634
Модели данных, используемые компонентом JTable 638
Визуализация ячеек таблицы 643
Обработка событий таблицы 645
15.5. Вывод на печать Swing-компонентов 649
Основы вывода на печать 649
Двойная буферизация и вывод на печать 651
Универсальная программа печати 652
Вывод на печать в JDK 1.3 656
15.6. Потоки Swing 658
Методы класса SwingUtilities 660
Глава 16. И с п о л ь з о в а н и е п о т о к о в 664
16.1. Запуск потоков 666
Выполнение действий в отдельном объекте Thread 666
Выполнение действий в классе, реализующем и н т е р ф е й с Runnable 668
16.2. Возникновение "гонок" 671
16.3. Синхронизация 673
Синхронизация фрагмента кода 673
Синхронизация метода 674
Ошибки, допускаемые п р и использовании синхронизации 675
16.4. Создание многопотоковых методов 677
16.5. Методы класса T h r e a d 681
Конструкторы 681
Константы 682
Методы 683
Остановка выполнения потока 688
16.6. Группы потоков 690
Конструкторы 690
Методы 690
16.7. Многопотоковая графика и двойная буферизация 692
Вывод графики в теле метода paint 693
Реализация динамической части изображения в виде отдельного
компонента 696
Рисование за пределами метода paint 696
Переопределение метода u p d a t e и использование метода
paint для инкрементного рисования 699
Использование двойной буферизации 703
16.8. Анимационные изображения 708
16.9. Таймеры 712
Конструктор 715
Методы класса Timer 716
16.10. Резюме 717
16 Содержание

Глава 17. С е т е в о е п р о г р а м м и р о в а н и е 718


17.1. Реализация клиента 720
П р и м е р универсального сетевого клиента 722
17.2. Разбор строк с помощью класса StringTokenizer 725
Класс StringTokenizer 726
Конструкторы 726
Методы 726
П р и м е р интерактивной программы разбора 727
17.3. П р и м е р клиента, предназначенного... 728
17.4. П р и м е р клиент-программы... 731
Класс, предназначенный для получения ресурса с указанного узла 732
Класс, предназначенный для получения ресурса по заданному URL 733
Выходные данные UrlRetriever 734
17.5. Класс URL 735
Получение ресурса по указанному URL 735
Методы класса URL 736
17.6.WebClient... 738
17.7. Реализация сервера 745
П р и м е р универсального сервера 747
Взаимодействие NetworkClient и NetworkSei'ver 750
17.8. П р и м е р простого HTTP-сервера 751
Сервер, выполняющийся в многопотоковом режиме 754
17.9. RMI: Remote Method Invocation 756
Создание RMTпpилoжeния 757
П р о с т о й пример использования RMI 758
Сервер, выполняющий численное интегрирование 762
Ч е т ы р е класса, необходимых для реализации примера 763
Компиляция и запуск примера 768
Конфигурация средств RMI для использования в корпоративной
системе 769
Компиляция и запуск примера для использования
в корпоративной системе 772
П р и м е р RMI-аплета 774

Ч А С Т Ь III. П Р О Г Р А М М Ы , В Ы П О Л Н Я Ю Щ И Е С Я
Н А С Т О Р О Н Е СЕРВЕРА 779
Глава 18. H T M L - ф о р м ы 780
18.1. Передача данных с помощью HTML-форм 781
18.2. Элемент FORM 785
18.3. Управляющие элементы для обработки текста 790
Поля редактирования 790
Поле ввода пароля 792
Текстовые области 792
18.4. Кнопки 794
Кнопка Submit 795
Кнопка Reset 797
Содержание 17

Кнопки JavaScript 798


18.5. Флажки и переключатели опций 799
Флажки опций 799
Переключатели опций 800
18.6. Раскрывающиеся списки и окна списков 802
18.7. Управляющий элемент... 805
18.8. Карты изображений на стороне сервера 806
IMAGE — стандартные карты изображений на стороне сервера 807
ISMAP — альтернативные карты изображений на стороне сервера 809
18.9. Скрытые поля 811
18.10. Группировка и н т е р ф е й с н ы х элементов 812
18.11. Порядок выбора элементов 813
Глава 19. Java на с т о р о н е с е р в е р а : с е р в л е т ы 816
19.1. Преимущества сервлетов... 818
Эффективность 818
Простота использования 819
Богатые возможности 819
Переносимость 819
Защита 819
Низкая стоимость 820
19.2. Инсталляция и настройка сервера 820
Программное обеспечение для поддержки сервлетов и JSP 820
Док}т^1ентация, необходимая для разработки сервлетов и JSP 822
И н ф о р м а ц и я о расположении файлов классов 822
О ф о р м л е н и е классов в виде пакета 822
Настройка сервера 823
Компиляция и установка сервлетов 823
Обращения к сервлетг1м 824
19.3. Базовая структура сервлета 824
Сервлет, генерирующий текстовое сообщение 825
Сервлет, генерирующий HTML-код 826
Простые \пгилиты для создания HTML-кода 828
19.4. Ж и з н е н н ы й цикл сервлета 830
Метод init 830
Метод service 831
Методы doGet, doPost и doXxx 832
И н т е р ф е й с SingleThreadModel 832
Метод destroy 833
19.5. П р и м е р использования инициализированных параметров 833
19.6. Запрос клиента: данные ф о р м ы 836
Получение данных ф о р м ы CGI-программами 836
Получение данных ф о р м ы сервлетами 836
П р и м е р получения значений трех параметров 837
Фильтрация данных, полученных в составе запроса 839
19.7. Запрос клиента: заголовок HTTP-запроса 840
Чтение полей заголовка сервлетами 841
18 Содержание

Создание таблицы, содержащей полученные поля заголовка 843


Поля заголовка, определенные в протоколе H T T P 1.1 845
Передача сжатых Web-страниц 848
19.8. Аналоги стандартных CGI-переменных 850
19.9. Ответ сервера: коды состояния 853
Установка кода состояния 853
Коды состояния H T T P 1.1 855
И н т е р ф е й с к различным поисковым серверам 860
19.10. Ответ сервлета: поля заголовка 864
Установка полей заголовка 865
Поля заголовка в составе ответа сервера 866
Постоянное состояние сервлета и автоматическая перезагрузка
Web-страниц 871
19.11. Cookie 880
Преимущества использования cookie 880
Проблемы, возникающие при использовании cookie 882
Средства API, предназначенные для работы с cookie 883
П р и м е р ы установки и чтения cookie 886
Вспомогательные средства для работы с cookie 889
Создание cookie с большим временем жизни 890
19.12. Поддержка сеанса 890
Необходимость поддержки сеанса 891
Модификация URL 891
API поддержки сеанса 892
Завершение сеанса 896
Сервлет, реализующий счетчик обращений клиента 897
Глава 20. JavaServer P a g e s 900
20.1. Общие сведения oJSP 901
20.2. Преимущества JSP 903
Преимущества JSP перед ASP и ColdFusion 903
Преимущества JSP перед Р Н Р 903
Преимущества JSP перед сервлетами 903
Преимущества JSP перед SSI 904
Преимущества JSP перед JavaScript 904
20.3. Элементы сценариев JSP 904
Выражения 905
Скриптлеты 908
Декларации 910
Предопределенные переменные 912
20.4. Директива page 913
Атрибут import 914
Атрибут сontentType 916
Атрибут isThreadSafe 917
Атрибут session 918
Атрибут buffer 918
Атриб)т autoflush 919
Содержание 19

Атрибут extends 919


Атрибут info 919
Атрибут errorPage 919
Атрибут isErrorPage 920
Атрибут language 920
XML-представление директив 920
20.5. Включение файлов и аплетов в состав JSP-документов 920
Директива include: включение файлов на этапе преобразования доку­
мента 921
Включение файлов в момент запроса 923
Включение аплетов, использующих Java Plug-In 925
Элемент jsp:fallback 929
20.6. Использование JavaBeans с JSP 932
Использование JavaBeans 933
П р и м е р класса StringBean 935
Установка свойств beans 937
Совместное использование компонентов bean 942
20.7. Определение новых JSP-дескрипторов 946
Компоненты, предназначенные для создания библиотеки дескрипторов 946
Определение простого дескриптора 949
Поддержка атрибутов дескриптора 953
Поддержка тела дескриптора 956
Необязательные данные в составе дескриптора 961
Обработка тела дескриптора 964
Многократная обработка тела дескриптора 968
Использование вложенных дескрипторов 970
20.8. Интеграция сервлетов и JSP 977
Перенаправление запросов 978
П р и м е р : интерактивный агент по обслуживанию туристов 981
Перенаправление запросов JSP-документами 988
Глава 21. А п л е т ы как и н т е р ф е й с к программам на с т о р о н е с е р в е р а 990
21.1. Передача данных с использованием запроса GET 992
21.2. И н т е р ф е й с к нескольким поисковым серверам 993
21.3. Использование запроса GET... 997
Ч т е н и е двоичных и ASCII-данных 997
Ч т е н и е сериализованных структур данных 999
21.4. Программа просмотра запросов... 1001
21.5. Использование запроса POST... 1007
21.6. Аплет, к о т о р ы й передает данные методом POST 1009
21.7. Взаимодействие без участия HTTP-сервера 1014
Глава 22. J D B C 1016
22.1. Основные этапы работы с JDBC 1017
Загрузка драйвера 1018
Определение URL для соединения 1019
Установление соединения 1019
Создание объекта Statement 1020
20 Содержание

Выполнение запроса 1020


Обработка результатов 1020
Закрытие соединения 1021
22.2. П р и м е р использования JDBC 1022
22.3. Вспомогательные классы для работы с JDBC 1027
22.4. Применение класса DatabaseUtilities 1035
22.5. Интерактивная программа просмотра запросов 1039
Код Q u e lyVie we г 1041
22.6. Заранее подготовленные выражения 1046
Глава 23. О б р а б о т к а XML-документов 1050
23.1. Разбор XML-документа посредством DOM Level 2 1052
Инсталляция и настройка 1052
Разбор 1053
23.2. П р и м е р использования DOM... 1055
23.3. Разбор XML-документов посредством SAX 2.0 1064
Инсталляция и настройка 1065
Разбор 1066
23.4. П р и м е р использования SAX: вывод общих сведений... 1067
23.5. П р и м е р использования SAX: подсчет заказов на книги 1073
23.6. Преобразование XML посредством XSLT 1077
Инсталляция и настройка 1078
Преобразование 1079
23.7. П р и м е р использования XSLT: редактор документов 1082
23.8. П р и м е р использования XSLT... 1090

Ч А С Т Ь rV^. J A V A S C R I P T 1099
Глава 24. JavaScript: д и н а м и ч е с к и и з м е н я ю щ и е с я Web-страницы 1100
24.1. Динамическая генерация HTML-документов 1103
Обеспечение совместимости с различными броузерами 1107
24.2. Обработка событий 1108
24.3. Синтаксис JavaScript 1109
Динамическая поддержка типов 1110
Объявление функций 1110
Объекты и классы 1111
Массивы 1116
24.4. Использование JavaScript при создании Web-страниц 1117
Определение размеров окна броузера 1117
Определение наличия дополнительных модулей 1120
24.5. Использование JavaScript для изменения... 1122
Динамическое изменение изображений 1122
Работа со слоями 1128
24.6. Использование JavaScript для проверки HTML-форм 1131
Индивидуальная обработка элементов ф о р м ы 1133
Проверка данных перед передачей на сервер 1135
24.8. Использование JavaScript для работы с фреймами 1144
Отображение во фрейхме ресурса с заданным URL 1144
Содержание 21

Получение фреймом фокуса ввода 1147


24.9. Обращение к Java из JavaScript 1148
Непосредственный вызов методов Java 1148
Использование аплетов для выполнения операций в JavaScript-сценариях 1150
Управление аплетами из JavaScript-сценария 1153
24.10. Доступ к средствам JavaScript из Java 1157
Определение цвета фона Web-страницы 1159
Аплет, проверяющий данные ф о р м ы 1160
Методы классаJSObject 1169
Глава 25. К р а т к о е р у к о в о д с т в о п о JavaScript 1172
25.1. Объект Array 1173
Конструкторы 1173
Свойства 1174
Методы 1174
Обработчики событий 1177
25.2. Объект Button 1177
Свойства 1177
Методы 1177
Обработчики событий 1178
25.3. Объект Checkbox 1178
Свойства 1179
Методы 1179
Обработчики событий 1180
25.4. Объект Date 1180
Конструкторы 1180
Свойства 1181
Методы 1181
Обработчики событий 1183
25.5. Объект Document 1183
Свойства 1183
Методы 1186
Обработчики событий 1186
25.6. Объект Element 1186
Свойства 1186
Методы 1187
Обработчики событий 1188
25.7. Объект FileUpload 1188
Свойства 1189
Методы 1189
Обработка событий 1189
25.8. Объект Form 1190
Свойства 1190
Методы 1190
Обработчики событий 1191
25.9. Объект Function 1191
Конструктор 1191
22 Содержание

Свойства 1191
Методы 1192
Обработчики событий 1192
25.10. Объект H i d d e n 1192
Свойства 1193
Методы 1193
Обработчики событий 1193
25.11. Объект History 1193
Свойства 1193
Методы 1194
Обработчики событий 1194
25.12. Объект Image 1194
Конструктор 1194
Свойства 1195
Методы 1196
Обработчики событий 1196
25.13. Объект JavaObject 1196
25.14. Объект JavaPackage 1197
25.15. Объект Layer 1197
Конструкторы 1197
Свойства 1197
Методы 1199
Обработчики событий 1200
25.16. Объект Link 1200
Свойства 1201
Методы 1202
Обработчики событий 1202
25.17. Объект Location 1202
Свойства 1202
Методы 1203
Обработчики событий 1204
25.18. Объект Math 1204
Свойства 1204
Методы 1205
Обработчики событий 1206
25.19. Объект MimeType 1207
Свойства 1207
Методы 1207
Обработчики событий 1207
25.20. Объект Navigator 1208
Свойства 1208
Методы 1210
Обработчики событий 1210
25.21. Объект N u m b e r 1210
Конструкторы 1211
Свойства 1211
Методы 1211
Содержание 23

Обработчики событий 1213


25.22. Объект Object 1213
Конструкторы 1213
Свойства 1214
Методы 1214
Обработчики событий 1214
25.23. Объект Option 1214
Конструкторы 1214
Свойства 1215
Методы 1215
Обработчики событий 1215
25.24. Объект Password 1215
Свойства 1216
Методы 1216
Обработчики событий 1216
25.25. Объект Plugin 1217
Свойства 1217
Методы 1218
Обработчики событий 1218
25.26. Объект Radio 1218
Свойства 1218
Методы 1219
Обработчики событий 1219
25.27. Объект RegExp 1219
Конструкторы 1219
Свойства 1221
Методы 1222
Обработчики событий 1223
Специальные символы в составе регулярных выражений 1223
25.28. Объект Reset 1225
Свойства 1225
Методы 1225
Обработчики событий 1226
25.29. Объект Screen 1226
Свойства 1226
Методы 1227
Обработчики событий 1227
25.30. Объект Select 1227
Свойства 1228
Методы 1229
Обработчики событий 1229
25.31. Объект String 1230
Конструктор 1230
Свойства 1230
Методы 1230
Обработчики событий 1235
25.32. Объект Submit 1235
24 Содержание

Свойства 1235
Методы 1236
Обработчики событий 1236
25.33. Объект Text 1237
Свойства 1237
Методы 1237
Обработчики событий 1238
25.34. Объект Textarea 1238
Свойства 1238
Методы 1239
Обработчики событий 1239
25.35. Объект Window 1240
Свойства 1240
Методы 1244
Обработчики событий 1249
П р е д м е т н ы й указатель 1254
Введение 25

ВВЕДЕНИЕ

В конце 1995 г. Марти Холл предложил новый курс для студентов, изучающих ком­
пьютерные науки в университете Джонса Хопкинса. В нем он рассмотрел основные
вопросы, имеющие о т н о ш е н и е к поддержке Web, с точки зрения Java-технологии.
Предполагалось, что слушатели курса будут изучать HTML, Java, HTTP, CGI-
программирование и JavaScript и участвовать в выполнении простых учебных проек­
тов. Вряд ли Марти представлял себе, какая судьба ожидает его курс. Появление пер­
вой части курса летом 1996 г. совпало со всплеском популярности Java. Аудитория бь.
ла переполнена, а число желающих, записавшихся в очередь, намного превышало ко­
личество мест в аудитории. Марти не успевал отвечать на т е л е ф о н н ы е звонки
студентов, пытавшихся доказать, что именно они непременно должны быть слушате­
лями нового курса. От п р е д п р и я т и й приходили заявки на материалы курса. Успех был
абсолютный.
В дальнеР1шем, когда Марти занимался поисками материала для продолжения кур­
са, он обнаружил, что, несмотря на обилие печатных изданий по каждому из рассмат­
риваемых вопросов, для того, чтобы полностью охватить материал курса, понадобит­
ся четыре или даже пять книг. В своей повседневной деятельности Марти постоянно
приходилось обращаться к огромному количеству собранных им публикаций. Напра­
шивалось решение объединить основной материал, необходимый профессиональ­
ным программистам, в одной книге.
Такова была предыстория первого издания книги Программирование для Web. Как и
следовало ожидать, она была очень популярна, но соответствующие технологии раз­
вивалось чрезвычайно быстро. Вместо HTML 3.2 в броузерах была реализована под­
держка HTML 4.0, была создана платформа Java 2, где появились средства, позволяю­
щие повышать э ф ф е к т и в н о с т ь программ, и новые графические библиотеки. Это да­
вало возможность создавать профессиональные коммерческие приложения. Раз­
работка JSP 1.0 стимулировала интерес и к сервлетам, и к JSP, как альтернативе не
только традиционным CGI-программам, но также ASP и ASP и ColdFusion. Появился
язык XML. П о с т е п е н н о область применения Java переместилась с персональных ком­
пьютеров на серверы.
По мере развития технологии стало ясно, что необходимо новое издание книги,
однако объем работы, требуемый для этого, был не по силам одному Марти. В про­
цесс подготовки нового издания включился Л э р р и Браун, обладающий огромным
опытом разработки и преподавания в области Java-пограмм и Web-технологий. Его
знания по вопросам Java Foundation Classes и созданию многопотоковых программ,
26 Введение

RMI и XML пришлись очень кстати. Вместе Марти и Л э р р и переработали сущест­


вующий материал, написали главы, посвященные HTML 4, C S S / 1 , H T T P L I и плат­
ф о р м е Java, заменили главы по CGI вопросами создания сервлетов и JSP, а также
включили новые разделы, в которых описали средства Swing и Java 2D. Кроме того,
они уделили внимание обработке XML-документов с помощью JAXP, DOM Level 2,
SAX 2.0 и XSLT. Работы было так много, что не хватало времени на сон.
Марти и Л э р р и искренне надеются, что результаты их работы окажутся полез­
ными для вас.

Реальный код для настоящих программистов


Данная книга предназначена для профессиональных разработчиков. Если вы хо­
тите узнать, как запустить броузер и получить список популярных Web-узлов, вы об­
ратились не по адресу. Если же вы программист и вас интересуют языки H T M L и
XML, Java-аплеты и сервлеты, JavaServer Pages и JavaScript, вы быстро найдете нужный
вам материал. Здесь описаны подходы к р е ш е н и ю часто встречающихся задач и опас­
ности, подстерегающие программиста на этом пути. В книгу включены п р и м е р ы , ил­
люстрирующие материал, в частности в ней содержатся коды более 250 Java-классов.
Мы постарались очень подробно описать часто используемые средства, кратко обсу­
дить вопросы, которые возникают реже, а получить "слишком специальные" сведения
читатель может в документации по API (доступной в электронном виде).
Приступая к работе, имейте в виду, что еще никто не стал великим программистом
лишь от того, что читал книги. Вам надо писать программы, и чем больше вы это бу­
дете делать, тем лучше. В кг1ЖДой главе мы сначала предлагаем простой п р и м е р , а за­
тем подробно рассматриваем необходимые вопросы на более сложных примерах.
Пропускайте разделы и целые главы, содержащие материал, для которого вы не ви­
дите применения, а затем возвращайтесь к ним по мере надобности.
Поступая так, как мы советуем, вы вскоре расширите свой кругозор и начнете уве­
р е н н о ориентироваться в решении проблем, возникающих перед программистами.
Вы сможете находить компромисс между требованием включать в Web-страницы но­
вые средства и необходимостью обеспечивать переносимость документов. Вы сможе­
те работать с фреймами, листами стилей и прочими возможностями, предоставляе­
мыми HTML 4.0. Вы научитесь создавать независимые переносимые графические
приложения. Вас не будет пугать задача создания Web-интерфейса к к о р п о р а т и в н о й
базе данных с помощью JDBC. Вы сможете обеспечить взаимодействие по сети с уда­
ленными приложениями. Вам станет ясно, как выполняется распараллеливание вы­
числений, как можно реализовать р е ш е н и е подзадачи в отдельном потоке или даже
на другом компьютере с использованием RML Вы научитесь оценивать, для решения
каких задач лучше подходят сервлеты, а для каких —JSP. Вы разберетесь в особенно­
стях протокола H T T P L1 так, что сможете использовать его для повышения эффек­
тивности работы ваших программ. Вы сможете реализовывать сложные программы
на стороне сервера, используя для этого компоненты JavaBeans и библиотеки деск­
р и п т о р о в JSP. Вы научитесь использовать JavaScript-сценарии для проверки содержи­
мого HTML-форм или для поддержки движущихся изображений. Ваш профессио­
нальный уровень повысится и, надеемся, существенно.
Введение 27

Структура книги
Данная книга разделена на четыре части, посвященные HTML, программированию
на языке Java, написанию программ, выполняющихся на стороне сервера и JavaScript.

Часть L Язык HTML


Для создания Web-страниц используется HTML (HyperText Markup Language).
HTML-документ включает обычный текст, предназначенный для отображения, и деск­
рипторы, описывающие содержимое Web-страницы. Дескрипторы интерпретируются
Web-броузерами, например Netscape Navigator и Microsoft Internet Explorer, при форма­
тировании Web-страниц. В данной части рассматриваются следующие вопросы.

• HTML 4.01. Элементы, определяемые последним из официальных HTML-


стандартов. Гипертекстовые ссылки, ш р и ф т ы , изображения, таблицы, карты
изображений на стороне клиента и др.
• Основные расширения Netscape и Internet Explorer. Перенаправление стра­
ниц, использование цветов и ш р и ф т о в , встроенные аудио-, видео- и ActiveX-
компоненты.
• Фреймы. Разбиение окна на прямоугольные области и связывание этих облас­
тей с HTML-документами. Фреймы без обрамления. Плавающие ф р е й м ы . Ука­
зание ф р е й м о в в гипертекстовых ссылках.
• Каскадные листы стилей. Листы стилей первого уровня для поддержки
ш р и ф т о в , цветов, изображений, ф о р м а т и р о в а н и я текста, организации отсту­
пов и др.

Часть IL Программирование на языке Java


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

• Средства Java. Ч т о отличает Java от других языков программирования? М и ф ы


о Java, и как обстоят дела в реальности.
• Объектно-ориентированное программирование на языке Java. П е р е м е н н ы е ,
методы, конструкторы, интерфейсы. Объявление класса и модификаторы. Па­
кеты, переменная окружения CLASS PATH и JAR-файлы.
• Синтаксис Java. П р о с т ы е типы, о п е р а т о р ы , строки, векторы, массивы, ввод-
вывод и класс Math.
• Графика. Аплеты. Приложения. Поддержка цвета и шрифтов. Область отсече­
ния. Загрузка и вывод изображений. Java Plug-In.
• Java 2D. Создание профессиональной высококачественной 2В-графики. Соз­
дание ф о р м , повторение изображений, использование локальных ш р и ф т о в ,
создание прозрачных ф о р м и преобразование координат.
28 Введение

• События, связанные с мышью и клавиатурой. Обработка событий. Типы со­


бытий. Высокоуровневые и низкоуровневые обработчики событий. Внутрен­
ние классы. Анонимные классы.
• Диспетчеры компоновки. FlowLayout, BorderLayout, GridLayout,
C a r d L a y o u t , G r i d B a g L a y o u t и B o x L a y o u t , Размещение компонентов вруч­
ную. Э ф ф е к т и в н о е использование диспетчеров компоновки.
• Компоненты AWT. C a n v a s , P a n e l , A p p l e t , S c r o l l P a n e , F r a m e , D i a l o g ,
F i l e D i a l o g и Window. Компонеьггы и контейнеры. Кнопки, флажки опций,
переключатели опций, списки, поля редактирования, текстовые области, ста­
тический текст, полосы прокрутки и раскрывающиеся меню. Загрузка и сохра­
нение окон с использованием сериализации объектов.
• Основные компоненты Swing. Создание аплетов и п р и л о ж е н и й с использова­
нием Swing. Изменение стиля интерфейса. Добавление обрамления к компо­
нентам. Использование H T M L при ф о р м и р о в а н и и статического текста и над­
писей на кнопках. Окна с сообщениями. Создание дочерних окон в приложе­
нии. Реализация Web-броузера средствами Swing.
• Расширенные средства Swing. J L i s t , J T r e e и J T a b l e . Использование произ­
вольных моделей данных и средств визуализации. Вывод компонентов Swing на
печать. Обновление компонентов Swing в многопотоковых программах.
• Создание многопотоковых программ. Создание потоков в отдельных или су­
ществующих объектах. Синхронизация доступа к разделяемым ресурсам. Груп­
пировка потоков. Вывод графики в многопотоковых программах и двойная бу­
феризация. Анимационные изображения. Управление таймерами.
• Сетевое программирование. Использование гнезд клиентами и серверами.
Класс URL. Универсальный сервер. Создание простого HTTP-сервера. Вызов
удаленных объектов средствами RMI.

Часть III. Программы, выполняющиеся на стороне


сервера
Программы, выполняющиеся на Web-сервере, могут генерировать динамические
документы на основе данных, передаваемых клиентами. Сервлеты можно рассматри­
вать как Java-альтернативу CGI-программам, а JSP — как альтернативу Active Server
Pages и ColdFusion. В данной части рассматриваются следующие вопросы.

• HTML-формы. Передача данных, введенных посредством HTML-форм. Эле­


менты, предназначенные для ввода текста. Кнопки. Флажки и кнопки выбора
опций. Карты изображений на стороне сервера. Скрытые поля. Порядок пе­
редачи фокуса.
• Java-сервлеты. Преимущества сервлетов перед другими технологиями. Жиз­
ненный цикл сервлета. Инициализированные параметры сервлетов. Получе­
ние данных ф о р м ы . Использование полей заголовка запроса и ответа, а также
кодов состояния H T T P L L Обработка записей cookie сервлетами. Поддержка
сеанса взаимодействия.
Введение 29

• JavaServer Pages (JSP). Преимущества JSP. Выражения, скриптлеты и деклара­


ции. Использование компонентов JavaBeans в JSP. Создание библиотек деск­
р и п т о р о в JSP. Сервлеты и JSP.
• Использование аплетов в качестве интерфейса при взаимодействии с
сервлетами. Передача данных GET и POST. НТТР-туннелирование. Использо­
вание сериализации объектов для передачи сложных структур данных между
аплетом и сервлетом. Отказ от использования HTTP-сервера.
• Java Database Connectivity (JDBC). Семь основных этапов соединения с базой
данных. Вспомогательные средства, облегчающие работу с JDBC. Представле­
ние результатов обращения к базе данных в текстовом виде и HTML-формате.
Средство просмотра запросов. Предварительно скомпилированные запросы.
• Обработка XML-документов средствами Java. Представление XML-документа
с помощью Document Object Model (DOM) Level 2. Обработка событий, связан­
ных с разбором XML-документа, посредством Simple API for XML Parsing (SAX)
2.0. Преобразование XML с помощью XSLT. Зависимость Java API for XML
Processing (JAXP) от производителя.

Часть IV. JavaScript


JavaScript — это язык, предназначенный для написания сценариев, встраиваемых в
Web-страницы. Сценарий интерпретируется при загрузке страницы. В последней
части данной книги рассматриваются следующие вопросы.

• Синтаксис JavaScript. Поля, методы, функции, строки, объекты, массивы и ре­


гулярные выражения.
• Настраиваемые Web-страницы. Адаптация под различные броузеры, реализа­
ции JavaScript и размеры экрана.
• Создание динамических Web-страниц. Анимационные изображения. Дейст­
вия со слоями. Обработка пользовательских событий.
• Проверка HTML-форм. Проверка изменяющихся компонентов ф о р м ы . Тес­
тирование данных, передаваемых на сервер.
• Поддерлска cookie. Чтение и установка значений. Объект C o o k i e .
• Управление фреймами. Передача результатов заданному фрейму. Запрет ис­
пользования фреймов при о т о б р а ж е н и и док)'ментов. Обновление нескольких
ф р е й м о в . Передача фокуса фрейму.
• Интеграция Java и JavaScript. Классы L i v e C o n n e c t и JSOb j e c t .
• Справочные материалы по JavaScript. Основные классы, определенные в
JavaScript 1.2. Поля, методы и обработчики событий. Document, Window, Form,
E l e m e n t , S t r i n g , Math, RegExp и п р о ч и е объекты.
30 Введение

Соглашения, принятые в книге


Программы, фрагменты программ и результаты их выполнения, а также иденти­
фикаторы, встречающиеся в тексте программы, выделены в данной книге моноши­
ринным ш р и ф т о м . Так, например. A p p l e t означает имя специального класса, ис­
пользуемого для создания аплетов.
Данные, вводимые пользователем, отображаются полужирным шрифтом. Для
ввода командной строки предлагается либо универсальное приглашение ( P r o m p t > ) ,
либо приглашение, специфическое для конкретной о п е р а ц и о н н о й системы (Unix>).
В приведенном ниже примере показаны данные, полученные при выполнении ко­
манды J a v a S o m e P r o g r a m .

Prompt> Java SomeProgram


Some O u t p u t
Стандартные способы решения задач кратко описаны во фрагментах текста,
оформленных так, как показано ниже.

М е т о д и к а профессионалов

Обратите особое внимание на фрагменты текста, помеченные как


"Методика профессионалов". Здесь описываются очень часто ис­
пользуемые подходы.

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

Web-узел
Материалы, имеющие отношение к данной книге, хранятся на Web-узле
http://www.corewebprogramming.com/
П о этому адресу вы найдете следующую информацию.

• Документированные исходные коды всех примеров, приведенных в книге. Эти


коды можно копировать и использовать без ограничений.
• Интерактивные версии всех HTML-страниц, Java-аплетов и сценариев JavaScript.
• Ссылки на все URL, упомянутые в книге.
• И н ф о р м а ц и я об условиях приобретения данной книги.
• Сообщения о курсах по Java.
• Дополнительная и н ф о р м а ц и я по данной книге.
• Инструментальные средства Ronco.
Об авторах 31

06 авторах
Марти Холл работает старшим научным сотрудником лаборатории прикладной фи­
зики исследовательского центра при университете Джонса Хопкинса и ведет курсы по
распределенным вычислениям и Web-технологии. Иногда он также проводит курсы на
предприятиях по использованию сервлетов, JavaSen^er Pages, а также по другим темам,
связанным с Web-технологиями. Он автор книг Core Servlets and JavaServer Pages, a также
первого издания книги Программирование для Web. С Марти можно связаться по адресу

Research and Technology Development Center


TheJohns Hopkins University Applied Physics Laboratory
11100Johns Hopkins Road
Laurel, MD 20723
hall@corewebprogramming. com
Л э р р и Браун работает старшим инженером в военно-морском исследовательском
центре и занимается разработкой Web-технологий для предприятий. О н также явля­
ется сотрудником факультета компьютерных наук университета Джонса Хопкинса,
где читает лекции по созданию программ, работающих на стороне сервера, распре­
деленному Web-программированию и разработке и н т е р ф е й с о в на базе Java. С Л э р р и
можно связаться по следующему адресу:
Naval Surface Warfare Center, Carderock Division
9500 MacArthur Boulevard
West Bethesda, MD 20817
brown@corewebprogramming. com
32 Благодарности

Благодарности
В подготовке книги к печати принимали участие очень многие. Без их помощи мы
вряд ли смогли бы выпустить даже первую часть. Д о н Олдридж, (Don Aldridge), Крис
Беннет (Chris B e n n e t t ) , Камил Белл ( С а т Ш е Bell), Пит Кларк (Pete Clark), Мария
Димеленте (Maria D i m a l a n t a ) , Нгуен-Хоа Дай ( N g u y e n - K h o a Duy), Дениз Еванс
(Denise E v a n s ) , Эми Карлсон (Amy K a r l s o n ) , Пол МакНеми (Paul M c N a m e e ) , Тодди
Норам (Toddi N o r u m ) , Уолтер Паскуинни (Walter P a s q u i n n i ) , Рич Слайвзек (Rich
Slywczak), Боб Т и н к е р (Bob Tinker) и Ким Топли (Kim Topley) указали нам на наши
ошибки и помогли советами. Без их участия данная книга вряд ли могла бы иметь ус­
пех. Мэри Лоу Н о р (Магу Lou Nohr) затратила много времени на расстановку недос­
тающих запятых и поис других ошибок. Благодаря ей текст книги стал таким, каким
вы его видите. Ванесса Мур ( V a n e s s a Moore) руководила подготовкой последней
версии. Ее работа осложнялась тем, чтоб н е к о т о р ы е изменения приходилось вносить
буквально в последний момент. Благодаря усилиям Ральфа Семмела ( R a l p h S e m m e l )
и Джулии Вессел ( J u l i e Wessel) был обеспечен гибкий график работы. Грег Д о н ч
(Greg D o e n c h ) из P r e n t i c e Hall всячески поощрял нашу работу над вторым издани­
ем. Всем перечисленным выше мы выражаем искреннюю благодарность.
Я, Марти, в особенности благодарен B . J . , Линдси и Натану за их понимание и
поддержку. Я, Л э р р и , благодарю Ли за поддержку и т е р п е н и е в те долгие дни, кото­
р ы е я проводил за компьютером.
Господь наградил нас обоих прекрасными семьями.

От издательства
Вы, читатель этой книги, и есть главный ее критик и комментатор. Мы ценим ва­
ше мнение и хотим знать, что было сделано нами правильно, что можно было сделать
лучше и что еще вы хотели бы увидеть изданным нами. Нам интересно услышать и
любые другие замечания, которые вам хотелось бы высказать в наш адрес.
Мы ждем ваших комментариев и надеемся на них. Вы можете прислать электрон­
ное письмо или просто посетить наш Web-сервер, оставив свои замечания, — одним
словом, любым удобным для вас способом дайте нам знать, нравится или нет вам эта
книга, а также выскажите свое мнение о том, как сделать наши книги более подходя­
щими для вас.
Посылая письмо или сообщение, не забудьте указать название книги и ее авторов,
а также ваш e-mail. Мы внимательно ознакомимся с вашим мнением и обязательно уч­
тем его при отборе и подготовке к изданию последующих книг. Наши координаты:
L-mail: i n f o @ w i l l i a i n s p u b l i s h i n g . com
WWW: h t t p : //www. w i l l i a m s p u b l i s h i n g . com
^:jzjs:-r±

ЯЗЫК HTML
Глава 1. Создание \/\/еЬ'Страниц с помощью HTML 4.0
Глава 2. Элементы блокового уровня HTML 4.0
Глава 3. Элементы текстового уровня HTML 4.0
Глава 4. Фреймы
Глава 5. Каскадные листы стилей
СОЗДАНИЕ
WEB-СТРАНИЦ
С ПОМОЩЬЮ
HTML 4.0

В ЭТОЙ главе...

• Общие сведения о HyperText Markup Language.


• HTML 4.0 и другие спецификации языка HTML.
• Проверка HTML-документов.
• Создание и публикация Web-страницы.
• Структура HTML-документа.
J~1y\ZJSJZJ

еред вами первая из пяти глав, посвященных HTML (HyperText Markup Lan­

П guage— язык разметки гипертекста). Прочитав их, вы познакомитесь с техно­


логиями, которые используются при создании Web-страниц. В первой главе
представлена структура HTML-документа, а в остальных четырех — дополнительные
сведения, которые позволят вам создавать профессиональные Web-страницы. В част­
ности, вы узнаете об элементах блокового уровня, элементах текстового уровня и о
каскадных листах стилей. В остальной части книги будут описаны Java-аплеты, гнезда
(socket), взаимодействие с базами данных, Java-сервлеты, JavaSetrver Pages (JSP) и об­
работка XML-документов. Эти технологии чрезвычайно важны для разработки высо­
кокачественных Web-узлов.
В данной главе мы рассмотрим историю возникновения HyperText Markup Language
и основные действия, необходимые для того, чтобы создать и опубликовать Web-
страницу. Затем мы сосредоточим внимание на общей структуре Web-страницы, опи­
шем основные HTML-элементы, обязательные для каждого документа, и обсудим уста­
новки, оказывающие влияние на документ в целом. Наконец, мы расскажем, как можно
проверить, соответствует ли готовая Web-страница стандарту HTML, и как включить в
документ информацию, предназначенную для использования поисковыми серверами.

1.1. HyperText Markup Language


Для создания Web-страниц используется HyperText Markup Language. Документ,
созданный на языке HTML, содержит обычный текст и дескрипторы, предназначен­
ные для разметки текста. С помощью дескрипторов можно описать внешний вид тек­
ста (например, указать, что некоторый фрагмент должен отображаться красным цве­
том) или его расположение на странице (например, задать размещение данных в
третьей и четвертой строках таблицы). Однако чаще всего дескрипторы описывают
содержимое документа (скажем, можно указать, что определенный фрагмент текста
является заголовком) и предоставляют броузеру решать, как именно следует размес­
тить текст на странице. В листинге L1 содержится HTML-код Web-страницы, пред-
36 Глава 1 . Создание Web-страниц с помощью HTML 4.0

ставленной на рис. 1.1. Если этот код непонятен вам, не беспокойтесь; элементы язы­
ка HTML мы детально обсудим в последующих главах. Однако даже с первого взгляда
можно заметить некоторые особенности HTML-документа. Так, например, из данно­
го листинга видно, что в нем содержится текст документа и набор дескрипторов, за­
ключенных в угловые скобки. Некоторые (но не все) языковые элементы представ­
ляют собой пары дескрипторов <ИМЯ> и </ИМЯ>.
На рис. 1.1 показано, как отображается Web-страница в окне конкретного броузера
(Internet Explorer 5.0), выполняющегося в операционной среде Windows 2000 Profes­
sional. Основные установки для данного броузера (начертание, размер и цвет символов)
заданы пользователем. Характеристики отображения, не указанные пользователем, оп­
ределяет сам броузер. Если Web-страница должна отображаться в броузерах различных
типов, попытки автора жестко задать внешний вид документа, как правило, заканчива­
ются неудачей. В главе 5 вы познакомитесь с каскадными листами стилей, которые пре­
доставляют авторам дополнительные возможности по управлению отображением Web-
страниц. Однако даже в этом случае автор не имеет полного контроля над внешним ви­
дом своих документов.

•а«е for lewrencc! М. шпнмш ~9тсжттк тттасштттш ..JOJJcj


Ele £dft ilew Favorites look jjelp
^ - *4 - : ^ ^ ^ -^Search JJFuvorte* ^Hetory -Ji' ^ Щ - J

d
Home Page for Lawrence M. Brown
Senior Network Engineer
Naval Surface Waifai-e Center
9500 MacArthor Boulevard
West Bethesda. Maryland. MD 20817-5700
email: lan7@corewebpro.eyttrmx^^ com
CAKOf KOcii^^oiYiSfON JNSI^f\/C^ Phone: (30 \) 277-4648

This IS my personal home page For more specific programmmg-related resoxirces pages, please see

'ёЗ

Рис. 1.1. Документ, код которого приведен в листинге 1.1, отоб­


ражается в окне броузера Internet Explorer 5.0, выполняющегося в
среде Windows 2000

Листинг 1.1. HTML-код простой \Л/еЬ-страницы

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<Title>Home Page for Lawrence M. Brown</Title>
</HEAD>
<BODY BGCOLOR="WHITE">
<H1 ALIGN="CENTER">Home Page for Lawrence M. Brown</Hl>
<HR>
<IMG SRC="images/navsea-nswc.gif" WIDTH=300 HEIGHT=117
HSPACE=10 VSPACE=5 ALIGN="LEFT" ALT="NSWC Logo">
Senior Network Engineer<BR>
<A HREF="http://www.dt.navy.mil/">
1 . 2 . HTML 4 . 0 и д р у г и е стандарты HTML 37

Naval Surface Warfare Center</A><BR>


9500 MacArthor Boulevard<BR>
West Bethesda, Maryland, MD 20817-5700<BR>
< I > e m a i l : < / I > <A H R E F = " m a i l t o : l a r r y @ c o r e w e b p r o g r a i r a n i n g . с о ш " >
larry@corewebprogramming.com</A><BR>
<I>Phone:</I> (301) 277-4648<BR CLEAR="ALL">
<P>
T h i s i s my p e r s o n a l home p a g e . For more s p e c i f i c
programming-related resources pages, please see:
< ! - - О с т а л ь н а я ч а с т ь Web-страницы з д е с ь не п р и в о д и т с я —>
</BODY>
</HTML>

На з а м е т к у

Попытки явно задать внешний вид документов, которые должны


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

П р и разработке Web-страниц могут возникнуть дополнительные трудности, свя­


занные с тем, что броузеры — не единственный тип программ, используемый для вос­
произведения HTML-документов. Различные приложения могут отображать, выво­
дить на печать, индексировать HTML-документы и даже синтезировать речь на осно­
ве их содержимого. Однако в данной книге мы рассмотрим лишь отображение
документов посредством WWW-броузеров.

1.2. HTML 4.0 и другие стандарты HTML


Помимо проблем, связанных с отображением в различных броузерах, авторы Web-
страниц должны принимать во внимание изменения в спецификации HTML. До января
1997 г. широко использовался стандарт HTML 2.0, разработанный W3C (World Wide
Web Consortium). Спецификация HTML 2.0 описывала возможности, типичные для
броузеров, созданных в середине 1994 г. Еще до того, как была опубликована HTML 2.0,
проводились работы по созданию спецификации нового поколения. Вначале эта спе­
цификация называлась HTML+, а затем получила название HTML 3.0. Однако основные
производители Web-броузеров не стали поддерживать эту спецификацию. В качестве
промежуточного варианта при переходе к новому стандарту была предложена специ­
фикация HTML 3.2 (в процессе разработки она имела кодовое название "Wilbur").
HTML 3.2 уместнее было бы назвать HTML 2.3, поскольку в ней были учтены не все
средства HTML 3.0. Наконец, в декабре 1997 г. появился стандарт HTML 4.0.
Следует заметить, что современные броузеры не поддерживают H T M L 4.0 в пол­
ном объеме. Наиболее соответствуют данной спецификации Netscape 4.0 и последую­
щие версии, а также Internet Explorer 4.0 и более поздние версии (реализации 3.x
данных броузеров поддерживали только HTML 3.2). П о сравнению с HTML 3.2 в
HTML 4.0 были внесены следующие изменения.
• Дополнительные возможности поддержки ф р е й м о в .
• Рекомендации по использованию листов стилей вместо непосредственного
форматирования элементов и атрибутов.
38 Глава 1 . Создание Web-страниц с помощью HTML 4.0

• Улучшенные средства выравнивания ячеек в таблицах и возможность группи­


ровки строк и столбцов.
• Поддержка событий, связанных с мышью и клавиатурой.
• Поддержка различных языков.
• Дополнительная поддержка невизуальных броузеров.
После п р и н я т и я в 1997 г. H T M L 4.0 в качестве стандарта была предложена специ­
ф и к а ц и я H T M L 4.01, в которой были исправлены незначительные ошибки предыду­
щей спецификации и добавлены н е к о т о р ы е атрибуты. HTML 4.01 была п р и н я т а W3C
24 декабря 1999 г. HTML 4.01 важна потому, что на ней базируется новая специфика­
ция X H T M L 1.0, предназначенная для поддержки XML в Web-страницах. В этом слу­
чае H T M L 4.01 используется для и н т е р п р е т а ц и и HTML-дескрипторов. XML позволя­
ет определять ф о р м а т ы данных для электронной коммерции, векторной графики и
многих других применений. П о д р о б н о язык XML будет обсуждаться в главе 23. Спе­
ц и ф и к а ц и я XHTML 1.0 была о ф и ц и а л ь н о принята 26 января 2000 г.
Электронные копии спецификации H T M L можно найти по следующим адресам:

H T M L 4.01
http://www.w3.org/TR/html401/

H T M L 4.0
h t t p : / / w w w . w 3 . o 3 r g / T R / 1 9 98/REC-html4 0 - 1 9 9 8 0 4 2 4 /

H T M L 3.2
http://www.w3.org/TR/REC-html32.html

1.3. Порядок публикации документа в Web


Для того чтобы опубликовать документ в Web, надо выполнить следующие действия.
1. Создать HTML-документ.
2. Разместить документ так, чтобы он был доступен из Web.
3. Убедиться в том, что HTML-код создан корректно и вид отображаемого доку­
мента соответствует вашим замыслам.

Создание документа
Поскольку в HTML-файле содержится ASCII-текст, для создания HTML-документа
можно воспользоваться обычным текстовым редактором, н а п р и м е р Notepad,
UltraEdit, emacs или BBEdit. Также документ можно создать с помощью HTML-
редактора, например, HomeSite, FrontPage или Dreamweaver. Наконец, можно авто­
матически преобразовать в HTML-формат документ, созданный в текстовом процес­
соре, таком как Microsoft Word или WordPerfect. Поскольку H T M L — это не язык опи­
сания страниц, а логический язык разметки, преобразованный документ может не­
сколько отличаться от документа, созданного посредством текстового процессора, и
потребовать небольшой коррекции.
1.3. Порядок публикации документа в Web 39

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


документов и обычно пользователь может самостоятельно выбирать шрифт и цвет
для отображения Web-страницы. В результате использование HTML-редакторов
"WYSIWYG" (What You See Is What You Get) затрудняется; no крайней мере, это спра­
ведливо при отсутствии каскадных листов стилей. С другой стороны, автор всегда
может сформировать документ с учетом установок по умолчанию конкретного бро­
узера. В любом случае HTML-редакторы экономят много времени при разработке
Web-страниц; большинство из них предоставляют визуальные средства для размеще­
ния таблиц и фреймов, многие редакторы поддерживают каскадные листы стилей.
Выбирая HTML-редактор, следует отдать предпочтение тому и них, который позво­
ляет непосредственно редактировать HTML-код. Дело в том, что при создании Web-
документа вы обязательно захотите использовать средства, которые не поддержива­
ются конкретным редактором и, следовательно, недоступны через его графический
пользовательский интерфейс. Перед тем как размещать готовую Web-страницу на
сервере, необходимо проверить синтаксис HTML-кода и убедиться, что внешний вид
документа не отличается от задуманного вами.

Размещение документа в Web


Для того чтобы HTML-документ был доступен Internet-пользователям, он должен
находиться на компьютере, подключенном к Internet, кроме того, на этом компьюте­
ре должен работать HTTP-сервер. Если у вас нет Internet-доступа, вам надо ознако­
миться с предложениями поставщиков Internet-услуг (провайдеров), которые публи­
куются в прессе и рекламных проспектах. Некоторые авторы создают свои Web-
страницы на том же компьютере, на котором они впоследствии будут размещены. Так
обычно поступают сотрудники коммерческих и некоммерческих организаций и учеб­
ных заведений. Нередко авторы создают Web-страницы на персональных компьюте­
рах дома или в офисе, а затем копируют их на сервер. В этом случае необходимо про­
верить, что каталог, в который скопирован документ, доступен из Internet. Подробно
этот вопрос обсуждается в последующих разделах.

Создание каталога для документа


На компьютере, на котором предполагается разместить Web-страницу, должен
выполняться HTTP-сервер. Подробно HTTP (HyperText Transfer Protocol — протокол
передачи гипертекстовой информации) будет обсуждаться в главе 19. Сейчас же вам
достаточно знать, что HTTP — это протокол, посредством которого WWW-клиенты
(броузеры) взаимодействуют с системами, на которых размещены Web-страницы.
Программа, обрабатывающая клиентские запросы, называется HTTP-сервером.
HTTP-сервер получает от клиента URL (Uniform Resource Locator — унифицирован­
ный локатор ресурсов), который можно рассматривать как "адрес" документа в Web, и
преобразует его в путь и имя файла в конкретной операционной системе. Обычно,
если клиент запрашивает файл из пользовательского каталога, сервер ищет его в под­
каталоге p u b l i c h t m l или www. Этот каталог не указан в URL, но отражает реальное
расположение файла. Конкретное имя каталога для размещения HTML-документов
автору сообщает системный администратор.
40 Глава 1 . Создание Web-страниц с помощью HTML 4.0

Часто провайдеры предоставляют авторам Web-страниц пространство на своих


серверах. В этом случае авторы передают HTML-документы на сервер провайдера по­
средством FTP. Как правило, перед тем как сообщить пользователю регистрационное
имя и пароль, администратор провайдера создает исходную структуру каталогов. Со­
трудники корпораций и учебных заведений обычно уже имеют учетные записи на
Web-сервере. Работая в системе UNIX, пользователь должен сначала создать каталог,
доступный из Web (как правило, имя этого каталога p u b l i c h t m l или www), а затем
скопировать в созданный каталог свои Web-страницы.
Предположим, что пользователь j a n e d o e собирается опубликовать документ
t e s t . h t m l на сервере www. s o m e - i s p . com. В этом случае ему надо скопировать файл
t e s t . h t m l в каталог / h o m e / j a n e d o e / p u b l i c _ h t m l . Здесь / h o m e / j a n e d o e — рабо­
чий каталог пользователя, а p u b l i c h t m l — специальный "скрытый" каталог, ис­
пользуемый Web-сервером. П р и обращении из Web URL данного документа будет
иметь вид h t t p : / / w w w . s o m e - i s p . c o m / ~ j a n e d o e / t e s t . h t m l .
Символ "--" обычно интерпретируется как "рабочий каталог пользователя". Неко­
т о р ы е программы используют кодировку ASCII или ISO Latin 1, поэтому в н е к о т о р ы х
случаях вместо "~" вы встретите последовательность символов "%7Е". Таким образом,
h t t p : / / s o m e . h o s t / % 7 E u s e r / p a t h — это допустимый URL.

Копирование файла в каталог


Если вы работаете на том же компьютере, на котором выполняется HTTP-сервер,
вы, наверное, будете создавать HTML-документы непосредственно в том каталоге, в
котором они должны быть размещены ( p u b l i c h t m l ) . Если вы предполагаете распо­
ложить документ на удаленном узле, для копирования файла вам придется воспользо­
ваться FTP-клиентом, например F e t c h — в системе Мае, f t p . e x e — в Windows
9 5 / 9 8 / 2 0 0 0 / N T или / u s r / b i n / f t p — в системе UNIX. Н е забывайте, что во многих
операционных системах имена файлов зависят от регистра символов.
В дополнение к "скрытому" каталогу по умолчанию многие HTTP-серверы использу­
ют имя файла по умолчанию. О н о применяется в том случае, когда в составе URL задан
только каталог, а файл не указан. Как правило, по умолчанию используются имена
i n d e x . h t m l . Welcome . h t m l и d e f a u l t . h t m l . Часто вместо . h t m l используется . htm.
Если исходная страница пользователя j a n e d o e расположена в файле / h o m e / j a n e d o e /
p u b l i c _ h t m l / i n d e x . h t m l , обращаясь к ней, достаточно указать h t t p : / / w w w . s o m e -
i s p . c o m / - j a n e d o e / . Можно также задать полный URL, включающий имя файла. Он
будет выглядеть так: h t t p : //www. s o m e - i s p . c o m / ~ j a n e d o e / i n d e x . h t m l .

Установка прав доступа к файлу и каталогу


Для того чтобы файл был доступен из Web, файл и каталог, в котором он располо­
жен, должны быть разрешены для чтения процессу HTTP-сервера. Поскольку HTTP-
сервер о б ы ч н о выполняется в непривилегированном режиме, данное требование оз­
начает, что доступ к файлу и каталогу должен быть разрешен всем пользователям.
Многие провайдеры, серверы которых работают под управлением UNIX, устанавли­
вают требуемые права по умолчанию, поэтому описанные ниже действия могут не по­
надобиться. Для того чтобы определить установки, пользователю UNIX достаточно
проверить значение umask. П р и необходимости требуемые права в системе UNIX мо­
гут быть установлены с помощью следующих команд:
1.4. Структура H T M L - д о к у м е н т а 41

Unix> cd
Unix> chmod a+x .
Unix> cd public_html
Unix> chmod a+x .
Unix> chmod a+r file
Поскольку Web-броузеры помимо HTML-документов позволяют также отображать
обычные текстовые ф а й л ы , для проверки доступа можно создать файл t e s t . t x t , со­
держащий одну строку текста, например последовательность символов "Hello!". Этот
файл надо поместить в соответствующий подкаталог вашего рабочего каталога и по­
пытаться обратиться к нему, задавая URL h t t p : / / y o u r . i s p . c o m / ' - y o u r -
a c c o u n t / t e s t . t x t . Если вы увидите в окне броузера содержимое данного файла, это
значит, что вам не надо предпринимать никаких действий по установке прав доступа.

Проверка документа
Разместив документ в Web, вам надо проверить, нет ли в нем синтаксических оши­
бок. Полезно просмотреть Web-страниц)' в окне броузера, но этого недостаточно. Дело
в том, что, встретив некорректный HTML-документ, броузер пытается "угадать", как он
должен выглядеть на самом деле. Однако различные броузеры могут интерпретировать
такой документ по-рг1зному, и не исключено, что фрагменты Web-страницы, которая
выглядит нормально в одном броузере, отобразятся совсем по-другому или вовсе не бу­
дут отображаться в другом. Так как HTML определяется посредством SGML (Standard
Generalized Markup Language— общий стандартный язык разметки), спецификация
SGML может быть использована для проверки соответствия документа синтаксису
HTML. Некоторые HTML-редакторы автоматически контролируют такое соответствие.
Кроме того, вы можете воспользоваться свободно распространяемыми программами
проверки. Самая популярная из них— W3C HTML Validator Service. Более подробная
информация расположена на следующих URL:
• http://validator.w3.org/
• http://www.htmlhelp.com/tools/validator/
• http://jigsaw.w3.org/css-validator/

М е т о д и к а профессионалов

"Доверяй, но проверяй": убедитесь в отсутствии синтаксических оши­


бок на ШеЬ'Странице, используя для этого программу проверки HTML

1-4. Структура HTML-доку мента


HTML-элементы определяются посредством дескрипторов, помещенных в угловые
скобки. Н а п р и м е р , <TITLE> представляет собой начальный, или открывающий, деск­
риптор элемента TITLE. Д е с к р и п т о р ы предоставляют броузеру описания элементов,
но не отображаются на экране. Дополнительная и н ф о р м а ц и я об HTML-элементах за­
дается посредством атрибутов, которые записываются в ф о р м е атрибут-значение. На­
пример, в дескрипторе <IMG S R C = ^ ' i m a g e s / s a m p l e . g i f " > последовательность
42 Глава 1 . Создание Web-страниц с помощью HTML 4.0

i m a g e s / s a m p l e . g i f является значением атрибута SRC элемента IMG. Если в составе


значения атрибута присутствуют символы, отличные от букв или ц и ф р , это значение
должно быть заключено в кавычки. Поэтому имеет смысл представлять в кавычках все
значения атрибутов, за исключением целых чисел. Все имена HTML-элементов и ат­
рибутов не зависят от регистра символов, однако это не относится к значениям атри­
бутов. Л и ш н и е пробелы, присутствующие в тексте документа, а также рядом с имена­
ми элементов и атрибутов, игнорируются, но в составе значений атрибутов пробелы
учитываются. Для сравнения, в спецификации XHTML L0 имена элементов должны
быть представлены символами нижнего регистра, а значения атрибутов, содержащие
ц и ф р ы , заключаются в кавычки. Это соответствует правилам XML (см. главу 23).
Н е к о т о р ы е элементы являются контейнерами, т.е. включают открывающий, или на­
чальный, дескриптор (например, <BODY>) и закрывающий, или конечный, дескриптор,
начинающийся символами " < / " (например, </BODY>). Другие элементы определяют­
ся лишь посредством открывающего дескриптора (например, <HR>). Если броузер не
может распознать HTML-элементы или атрибуты, он игнорирует их. Текст документа
всегда обрабатывается, даже если он содержится между недопустимыми открываю­
щим и закрывающим дескрипторами. Комментарии помещаются между наборами
символов " < ! — " и " - - > " . Текст комментариев может располагаться в нескольких
строках, но не должен включать символы " - - " .

Шаблон HTML -документа


HTML-документ начинается с декларации DOCTYPE, в которой задается версия
HTML. И н ф о р м а ц и я о версии используется броузером или программой проверки.
После DOCTYPE следует элемент HTML, содержащий элементы HEAD и BODY. Элемент
HEAD, в свою очередь, содержит элемент TITLE. Элемент BODY, непосредственно сле­
дующий за элементом HEAD, о б ы ч н о начинается с заголовка первого уровня (HI), за
которым располагается содержимое Web-страницы.
П о правде говоря, из всех перечисленных компонентов шаблона обязательны
лишь выражение DOCTYPE и элемент TITLE. Элементы HEAD и BODY подразумеваются
броузером. Однако начинать разработку HTML-документа рекомендуется с вышена­
званного шаблона. HTML-редактор можно сконфигурировать так, что он будет авто­
матически включать необходимые дескрипторы при открытии нового документа.
На рис. L2 показан внешний вид шаблона HTML-документа, представленного в лис­
тинге L2. С такого шаблона удобно начинать создание всех Web-страниц, соответст­
вующих спецификации HTML 4.0 (за исключением документов с фреймами, описанных
в главе 4).
-iq|.xf
file Ее» view €о Coiwwjnfc«tor Шр

^ i' а Л ^ - й1 '> ii^ Э i


Main Heading

pocument: Oortc
.,M .:^:..
Рис. 1.2. Шаблон HTML 4.0, отображаемый в окне
броузера Netscape 4.7, который выполняется в
среде Windows 2000 Professional
1.4. Структура НТМЬ-документа 43

Листинг 1.2. HTML4 . 0~Template, html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Title</TITLE>
</HEAD>

<BODY>
<Hl>Main Heading</Hl>
<!-- Остальная часть Web-страницы -->
</BODY>
</HTML>

Декларация DOCTYPE
в зависимости от содержимого HTML-документа могут использоваться т р и вари­
анта декларации DOCTYPE.

Строгая д е к л а р а ц и я H T M L 4.01 D O C T Y P E
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">

П р о м е ж у т о ч н а я д е к л а р а ц и я H T M L 4.01 D O C T Y P E

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 1 Transitional//EN">

Д е к л а р а ц и я H T M L 4.01 D O C T Y P E д л я р а б о т ы с ф р е й м а м и

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 1 Frameset//EN">


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

Э л е м е н т ы H T M L 4.01
http://www.w3.org/TR/html4/index/elements.html

Атрибуты H T M L 4.01
http://www.w3.org/TR/html4/index/attributes.html

Декларация H T M L для работы с ф р е й м а м и указывается в тех документах, к о т о р ы е


оформляются как набор ф р е й м о в (frame).
44 Глава 1 . Создание Web-страниц с помощью HTML 4.0

Н и ж е приведено подробное описание элемента H T M L и его атрибутов. В данной


книге для представления элементов языка HTML используется единый формат. Имя
элемента сопровождается списком обязательных атрибутов. Многоточие (...) означа­
ет, что в составе открывающего дескриптора могут присутствовать дополнительные
сведения или необязательные атрибуты. За открывающим дескриптором следует за­
крывающий дескриптор, либо явно указывается, что закрывающий дескриптор отсут­
ствует. Атрибуты перечисляются в том порядке, в каком о н и наиболее часто встреча­
ются в реальных документах. Атрибуты, не соответствующие спецификации HTML 4.0,
но поддерживаемые Netscape или Internet Explorer, помечаются как "нестандартные".
Предполагается, что каждый HTML-элемент и атрибут опробован в английской вер­
сии Netscape 4.08-4.7 либо Internet Explorer 4.01-5.5.

Элемент: <HTML •..> ... </HTML>


Атрибуты: LANG, DIR, VERSION
Элемент HTML непосредственно следует за выражением DOCTYPE и представляет
собой контейнер, содержащий элементы HEAD и BODY. В составе дескриптора HTML мо­
гут присутствовать атрибуты, с помощью которых задаются язык документа, направле­
ние текста и HTML-версия кода. В большинстве случаев открывающий и закрывающий
дескрипторы HTML могут быть безболезненно удалены, поскольку броузеры по исход­
ному коду документа определяют, что он создан на языке HTML. Однако, для того что­
бы документ соответствовал стандарту, дескрипторы HTML должны быть указаны.

LANG
Атрибут LANG представляет в закодированном виде язык, на котором создан доку­
мент. Современные броузеры интерпретир)тот атрибут LANG как источник дополни­
тельной информации, не влияющей на порядок представления документа. Однако
сведениями, содержащимися в атрибуте LANG, могут воспользоваться поисковые
серверы и средства проверки орфографии. Список кодовых названий языков вы
найдете в документе RFC 1766. Чтобы скопировать RFC, можно обратиться к одном}^
из архивов, описанных в документе h t t p : //www. r f c - e d i t o r . o r g / .

DIR
Атрибут DIR определяет направление следования текста: LTR (left-to-right— слева
направо) или RTL (right-to-left— справа налево). Internet Explorer поддерживает
атриб)^ DIR, но в зависимости от кодировки направление ASCII-символов может
изменяться. Поскольку данный атрибут не поддерживается Netscape (в частности,
Netscape 4.7 и более ранними версиями), его следует использовать только в сетях
intranet, о р и е н т и р о в а н н ы х исключительно на Internet Explorer.

Внимание!

Netscape не поддерживает атрибут DIR элемента HTML,

VERSION
Атрибут VERSION задает версию HTML для документа. Такая и н ф о р м а ц и я избы­
точна и данный атрибут применяется редко. Версию H T M L броузер может опре-
1.5. Элемент HEAD — высокоуровневая информация... 45

делить из выражения DOCTYPE. Стандартом H T M L 4.0 атрибут VERSION не реко­


мендован для применения.

Спецификация HTML предполагает использование атрибутов LANG и DIR во всех


элементах, за исключением APPLET, BASE, BASEFONT, BR, FRAME, FRAMESET,
IFRAME, NOFRAMES, PARAM и SCRIPT. Поэтому LANG и DIR отдельно обсуждаться не
будут и рассмотрение их заканчивается в рамках элемента HTML.

1.5. Элемент HEAD — высокоуровневая


информация о Web-странице
Элемент: <HEAD>.,. </HEAD>
Атрибуты: PROFILE
Раздел HEAD — первый раздел HTML-документа. Он располагается после деклара­
ции DOCTYPE и открывающего дескриптора <HTML>, перед разделом BODY. В составе
HEAD должен присутствовать элемент TITLE, который часто является единственным
элементом данного раздела. Атрибут PROFILE не влияет на отображение документа в
окне броузера, но предоставляет и н ф о р м а ц и ю , которой могут воспользоваться поис­
ковые серверы, броузеры, не отображающие документы, серверы индексации и дру­
гие программы. Атрибут PROFILE определяет URL файла, предоставляющего допол­
нительную и н ф о р м а ц и ю поисковым серверам для разбора метаданных в разделе HEAD
(элемент МЕТА будет обсуждаться далее в этом разделе). Спецификация H T M L также
предусматривает для элемента HEAD атрибуты LANG и DIR. Эти два атрибута были рас­
смотрены выше.

Обязательный элемент в составе HEAD


Элемент: <TITLE> ... </TITLE>
Атрибуты: отсутствуют
Элемент TITLE обязателен в составе документа HTML 4.0 и содержит текст заголов­
ка. В состав TITLE не мог)^ входить другие HTML-дескрипторы, однако данный элемент
может включать текстовые примитивы, например &сору для отображения символа "©"
(обратитесь к табл. 2.1 в главе 2). Содержимое элемента TITLE представляется в заго­
ловке окна броузера. Кроме того, Netscape и Internet Explorer отображают текст, нахо­
дящийся в составе TITLE, в верхней части страницы, выводимой на печать. Следует
различать элемент TITLE и основной заголовок Web-страницы. Содержимое элемента
TITLE, в отличие от основного .заголовка, не отображается в составе самого документа.
Спецификация HTML 4.0 позволяет указывать в открывающем дескрипторе TITLE ат­
рибуты LANG и DIR, но они не поддерживаются Netscape и Internet Explorer. Использо­
вание символов, не принадлежащих набору ASCII, приведет к тому, что в заголовке окна
будет отображаться имя файла, содержащего код документа.
46 Глава 1 . Создание Web-страниц с помощью HTML 4.0

Необязательные элементы в составе HEAD


в дополнение к элементу TITLE в состав HEAD могут быть включены элементы
BASE, МЕТА, BGSOUND и LINK. Как правило, эти элементы используют на своих Web-
страницах о п ы т н ы е авторы. Если вы только начинаете работу с HTML, вам лучше от­
казаться от п р и м е н е н и я указанных средств до тех п о р , пока вы не приобретете доста­
точные навыки в использовании более важных и чаще встречающихся элементов.

Элемент: <BASE HREF="..." ...> (закрывающий дескриптор отсутствует)


Атрибуты: HREF (обязательный), TARGET
Элемент BASE определяет базовую позицию для относительных URL. Относительными
называются не полностью заданные URL, в которых присутствует имя файла, но не ука­
зан протокол или имя узла. Базовой позицией считается каталог, из которого был за­
гружен текущий документ. Если документ будет скопирован на др)той узел, а вспомога­
тельные документы останутся на прежнем месте, с помощью элемента BASE можно до­
биться, чтобы относительные URL по-прежнему указывали на корректные позиции.
Атрибут HREF явно указывает базовую позицию для всех относительных URL. Предпо­
ложим, что документ h t t p : / / w w w . m i c r o s o f t . c o m / w i n d o w s 2 0 0 0 . h t m l должен быть
скопирован на зеркальные серверы www. a p p l e . com и www. s u n . com. В этом случае мож­
но использовать элемент BASE. В результате элемент HEAD приобретет следующий вид:
<HEAD>
<TITLE>Why You S h o u l d Buy Windows 2000</TITLE>
<BASE H R E F = " h t t p : / / w w w . m i c r o s o f t . c o m / w i n d o w s 2 0 0 0 / " >
</HEAD>
Атрибут TARGET может применяться для отображения выбранных ссылок в новом
окне либо, если используются ф р е й м ы , позволяет задать ф р е й м по умолчанию для
отображения документа, на который указывает выбранная ссылка. Часто авторы Web-
страниц включают в свои документы выражение <BASE TARGET=" . . . "> (без обяза­
тельного атрибута HREF), чтобы перенаправить все ссылки на странице в указанный
фрейм. Подробно работа с фреймами будет обсуждаться в главе 4.

Элемент: <МЕТА ...> (закрывающий дескриптор отсутствует)


Атрибуты: NAME, CONTENT (обязательные), HTTP-EQUIV, SCHEME
Элементы МЕТА позволяют записывать и н ф о р м а ц и ю о документе, перенаправлять
и обновлять Web-страницы, а также включать звуковые файлы. К о н к р е т н ы й способ
использования дескрипторов МЕТА для записи и н ф о р м а ц и и о документе изменяется в
зависимости от системы. Как привило, данные в дескрипторе МЕТА задаются в виде
пар NAME-CONTENT, где NAME представляет имя свойства, а CONTENT — его значение.
Обычно с именем связываются такие записи, как a u t h o r (автор документа), d e ­
s c r i p t i o n (краткое описание), k e y w o r d s (ключевые слова, разделенные запятыми,
предназначенные для использования поисковыми серверами) и g e n e r a t o r (програм­
ма, с помощью которой документ был сгенерирован). Такие поисковые серверы, как
Google, Infoseek и Lycos, используют запись k e y w o r d s для индексирования (но игно­
рируют всю запись, если слово встречается больше семи раз). Запись d e s c r i p t i o n
эти серверы используют, предоставляя пользователю описание документа. В листинге
1.3 демонстрируется применение дескриптора МЕТА.
1.5. Э л е м е н т HEAD — высокоуровневая и н ф о р м а ц и я . . . 47

Листинг 1.3. Пример раздела HEAD, содержащего дескриптор МЕТА

<HEAD>
<TITLE>Why You Should Buy Windows 2000</TITLE>
<BASE HREF="http://www.microsoft.com/windows2000/">
<META NAME="author" CONTENT="Bill Gates">
<META NAME="keywords"
CONTENT="Windows,Advocacy,OS,Operating Systems">
<META NAME="description"
CONTENT="A summary of the advantages of Windows 2000.">
</HEAD>

Атрибут HTTP-EQUIV позволяет обновлять страницы, используя значение R e f r e s h .


Предположим, что электронная версия газеты содержит заголовки, которые время от
времени изменяются. Чтобы отобр?13Ить эти изменения, страница должна обновляться
каждые 30 минут (1800 секунд). Предположим также, что одна из Web-страниц находит­
ся по адресу h t t p : / / w w w . m i c r o s o f t . c o m / w i n d o w s 2 0 0 0 / B u y - W i n 2 0 0 0 . h t m l . В лис­
тинге 1.4 показано, как может быть использован дескриптор МЕТА для автоматического
обновления документа на экране. Для этого задается выражение НТТР-
EQUIV=" Re f r e s h " , а в качестве значения атрибута CONTENT указывается время (1800
секунд), по истечении которого страница должна быть перезагружена.

Л и с т и н г 1.4. Web-страница автоматически перезагружается каждые 3 0 минут

<HEAD>
<TITLE>Why You Should Buy Windows 2000</TITLE>
<META HTTP-EQUIV="Refresh" CONTENT="1800">
</HEAD>

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


расположенный по другому URL (листинг 1.5). Такой подход применяется тогда, ко­
гда URL документа изменился и автор собирается автоматически перенаправить
пользователя к новому URL. Новая страница автоматически отобразится на экране
броузера по истечении 5 секунд. Ч т о б ы это произошло, надо задать значение атрибу­
та HTTP-EQUIV, равное R e f r e s h , и указать новый URL документа. П р и этом в неко­
т о р ы х броузерах после щелчка на кнопке Взск снова отобразится документ, содер­
жащий дескриптор МЕТА. Этот дескриптор снова перенаправит пользователя к тому
документу, который он просматривал перед тем, как активизировать кнопку Взск.

Л и с т и н г 1.5. Перенаправление по новому адресу

<HEAD>
<TITLE>Why You Should Buy Windows 2000 (New Address)</TITLE>
<META HTTP-EQUIV="Refresh"
C0NTENT="5;
URL=http://www.apple.com/Buy-Win2000.html">
</HEAD>
48 Глава 1 . Создание Web-страниц с помощью HTML 4.0

Если заданный URL указывает на звуковой файл в формате, который поддержива­


ется клиентской программой, некоторые броузеры начинают воспроизводить такой
файл. Для организации звукового сопровождения существуют также другие способы.
Так, Internet Explorer поддерживает элемент BGSOUND (который будет описан ниже);
кроме того, и Netscape, и Internet Explorer позволяют использовать для некоторых
типов звуковых файлов элемент EMBED. Однако будьте осторожны: если при посеще­
нии узла автоматически начинается воспроизведение звука, пользователь может вос­
принять это как ошибку.
Атрибут HTTP-EQUIV позволяет также задавать значение CHARSET для представ­
ления текста в окне броузера. Так, например, следующий дескриптор МЕТА указывает
на то, что для всего текста преобразование байтовой последовательности в символы
должно выполняться согласно кодировке GB2312:

<МЕТА HTTP-EQUIV="Content-type"
CONTENT="text/html; CHARSET=GB2312">
Стандарты ISO определяют отображение символов для многих языков. Практиче­
ски все наборы включают стандартные ASCII-символы, поэтому в составе документа,
составленного на любом языке, можно расположить английский текст. Если документ
содержит текст на нескольких языках, его надо представить с помощью символов в
16-битовом ф о р м а т е Unicode. В настоящее время широко распространен вариант
Unicode UTF-8, где каждый 16-битовый символ Unicode преобразуется в байтовую по­
следовательность, причем ASCII-символ занимает лишь 8 бит. Дополнительную ин­
ф о р м а ц и ю о применении Unicode для представления документов на нескольких язы­
ках можно найти по адресу h t t p : //wwv/. U n i c o d e . o r g / .
С помощью HTTP-EQUIV можно управлять содержимым заголовка HTTP-ответа,
значение заголовка задается с помощью CONTENT. HTTP-EQUIV представляет собой
простое расширение (поддерживаемое как Netscape, так и Internet Explorer), которое
"устанавливает" заголовок, не прибегая к его генерации. Подробная и н ф о р м а ц и я о
HTTP-заголовках приведена в главе 19.
Атрибут SCHEME задает формат значения свойства, представленного с помощью
пары NAME-CONTENT дескриптора МЕТА. Здесь также NAME определяет имя свойства, а
CONTENT — его значение. В приведенном ниже примере атрибут SCHEME указывает,
что дата задана в формате месяц-день-год (SCHEME="Month-Day-Year"), в отличие
от европейского формата день-месяц-год.
<МЕТА SCHEME="Month-Day-Year"
NAME="Date"
VALUE="05-01-2000">
Атрибуты LANG и DIR элемента МЕТА игнорируются.

Элемент: <BGSOUND SRC="..." ...> (закрывающий дескриптор


отсутствует)
Атрибуты: SRC (обязательный), LOOP
Это нестандартный элемент, поддерживаемый Internet Explorer и предназначен­
ный для воспроизведения звуковых файлов. Элемент BGSOUND может присутствовать
как в составе элемента BODY, так и в элементе HEAD.
1.5. Элемент HEAD — высокоуровневая информация... 49

SRC
Атрибут SRC задает URL звукового файла, который должен быть представлен в
формате . wav, . a u или MIDI.

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

Элемент: <SCRIPT TYPE="..." ...> ... </SCRIPT>


Атрибуты: LANGUAGE, SRC, ТУРЕ ( о б я з а т е л ь н ы й ) , CHARSET, DEFER
ЭлСхМент SCRIPT применяется для включения программ, которые обычно написаны
на языке JavaScript. Подробно программы на JavaScript будут рассмотрены в главе 24.

Элемент: <STYLE TYPE="..." ...> ... </STYLE>


Атрибуты: TYPE (обязательный), TITLE, MEDIA
Элемент STYLE определяет каскадные листы стилей — чрезвычайно удобное сред­
ство, позволяющее задавать ш р и ф т ы , цвета, изображение заднего плана, границы и
другие характеристики, к о т о р ы е применяются к различным элементам документа.
Подробно листы стилей будут обсуждаться в главе 5.

Элемент: <LINK ...> (закрывающий дескриптор отсутствует)


А т р и б у т ы : HREF, REL, REV, TYPE, CHARSET, HREFLANG, MEDIA, ONCLICK, ONDBLCLICK,
ONMOUSEDOWN, ONMOUSEUP, ONMOUSEOVER, ONMOUSEMOVE, ONMOUSEOUT, ONKEYPRESS,
ONKEYDOWN, ONKEYUP, TARGET. TITLE, I D , CLASS, STYLE
Элемент LINK предоставляет и н ф о р м а ц и ю о месте данной страьп^цы в наборе до-
K}TvieHTOB. П р и этом определяются расположение таблицы, определяющей содержа­
ние, предыдущий и последующий документы набора и другие данные. В разделе HEAD
может содержаться несколько элементов LINK.

HREF, REL, REV


Атрибут HREF задает URL связанного документа. Атрибут REL определяет отноше­
ние связанного документа к текущему, а REV задает отношение текущего документа
к указанному. Наиболее часто встречаются отношения CONTENTS, INDEX,
GLOSSARY, HELP, NEXT и PREVIOUS (они используются для перемещения между
документами), MADE (определяет автора документа) и STYLESHEET (для связи с
каскадными листами стилей). Н и ж е приведен пример использования указанных
атрибутов.
<LINK REL=STYLESHEET
HREF="My-Styles.CSS"
TYPE="text/css">
50 Глава 1 . С о з д а н и е W e b - с т р а н и ц с п о м о щ ь ю HTML 4 . 0

TYPE, CHARSET, HREFLANG, MEDIA


Атриблт TYPE задает MIME-тип ресурса, на который указывает ссылка. П р и м е р о м
определения МШЕ-типа может служить выражение TYPE=" t e x t / h t m l " . MIME-
т и п ы , описанные в документе RFC 1521 (список архивов, содержащих наборы
RFC, находится по адресу h t t p : / / w w w . r f c - e d i t o r . o r g / ) , определяют ф о р м а т ы
различных типов файлов, например t e x t , pdf, g i f . Атрибут CHARSET определяет
кодировку символов для документа, на к о т о р ы й указывает ссылка. П р и м е р о м мо­
жет служить выражение CHARSET="ISO-8859-6". Атрибут HREFLANG задает язык
связанного документа. Н а п р и м е р , выражение HREFLANG="pt" сообщает, что до­
кумент, на который указывает ссылка, написан на португальском языке. Атрибут
MEDIA задает среду для воспроизведения связанного документа. Допустимыми зна­
чениями являются ALL, AURAL, BRAILLE, HANDHELD, PRINT, PROJECTION, SCREEN
(no умолчанию), SPEECH, TTY и TV.

Остальные атрибуты элемента LINK не поддерживаются современными броузера­


ми и в данной книге не рассматриваются.

1.6. BODY — основная часть документа


HTML-документ должен содержать один раздел BODY, определяющий содержимое
Web-страницы. Исключением являются документы, в которых используются ф р е й м ы .
П р и работе с фреймами в документе верхнего уровня задается расположение фрей­
мов и указывается, в каком из них должен быть отображен тот или иной документ.
Элемент BODY в этом случае не используется. Поскольку содержимое элемента TITLE
отображается лишь в заголовке окна, но не представляется в составе документа и не
выводится на печать, элемент BODY обычно начинается с основного заголовка, кото­
р ы й формируется посредством дескриптора <Н1>. Как правило, в основном заголовке
документа задается та же и н ф о р м а ц и я , что и в элементе TITLE. Использование эле­
мента TITLE и главного заголовка представлено в листинге 1.6.
В разделе BODY присутствуют два основных класса HTML-элементов: элементы
блокового и текстового уровней. Элементы блокового уровня, как правило, прекра­
щают естественный порядок следования текста и продолжают его отображение с но­
вой строки. Примерами элементов блокового уровня являются заголовки, абзацы тек­
ста, списки, таблицы, горизонтальные л и н и и , используемые как разделители, и фор­
мы ввода. Практически в любую точк}^ элемента блокового уровня может быть
включен элемент текстового уровня. Кроме того, в их состав могут входить другие
элементы текстового уровня (например, пункты списка или ячейки таблиц). Элемен­
ты блокового уровня мы рассмотрим в главе 2.

Л и с т и н г 1.6. Шаблон документа HTML 4 . 0 с основным заголовком

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>My First Web Page</TITLE>
</HEAD>
<BODY>
1.6. BODY — основная часть документа 51

<Н1>Му First Web Page</Hl>


< ! — Остальная часть HTML-документа -->
</BODY>
</HTML>

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


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

Элемент: <BODY ...> ... </BODY>


Атрибуты: BACKGROUND, BGCOLOR, TEXT, LINK, VLINK, ALINK, TITLE, ONCLICK,
ONDBLCLICK, ONMOUSEDOWN, ONMOUSEUP, ONMOUSEMOVE, ONMOUSEOUT, ONKEYPRESS,
ONKEYDOWN, ONLOAD, ONUNLOAD, ONFOCUS (нестандартный), ONBLUR (нестандартный),
ONERROR (нестандартный), ONMOVE (нестандартный), ONRESIZE (нестандартный),
ONDRAGDROP (нестандартный), BGPROPERTIES (нестандартный), CLASS, ID, STYLE
Элемент BODY часто используется без атрибутов (т.е. открывающий дескриптор име­
ет вид <BODY>), однако в его состав могут входить необязательные атрибуты, опреде­
ляющие фоновое изображение, цвет фона, текста и гипертекстовых ссылок (обычно
ссылки, указывающие на документы, которые ранее отображались в окне броузера, и
ссылки, указывающие на документы, которые не просматривались, представляются
разными цветами). Если броузер поддерживает JavaScript, он также позволяет задавать
атрибуты, определяющие код, который выполняется при определенных условиях.

BACKGROUND и BGCOLOR
Атрибут BACKGROUND задает URL графического файла. Изображение, содержа­
щееся в этом файле, отображается в качестве ф о н а для Web-документа. Если раз­
меры изображения меньше размера окна, оно многократно повторяется. Возмож­
ность многократного воспроизведения изображения позволяет снизить время за­
грузки фона. Так, например, если в качестве ф о н а используются оттенки цвета,
плавно изменяющиеся от левой границы окна до его правой границы, то в качест­
ве фонового должно использоваться изображение большой ширины, но размер
его по высоте может быть равен одному пикселю. Помимо BACKGROUND, рекомен­
дуется также задавать атрибут BGCOLOR, определяющий цвет фона. Использование
BGCOLOR— это своеобразная "страховка" на случай, если клиент-программа по ка­
ким-то причинам не сможет скопировать ф о н о в о е изображение.
В большинстве броузеров по умолчанию используется белый (WHITE, или #FFFFFF)
либо светло-серый (SILVER, или #СОСОСО) фон. Несмотря на то что BACKGROUND и
BGCOLOR широко используются, и тот и другой атрибут не рекомендованы для при­
менения. Фоновое изображение и цвет фона желательно задавать с помощью листов
стилей.
52 Глава 1 . Создание Web-страниц с помощью HTML 4.0

TEXT, LINK, VLINK и ALINK


Данные атрибуты задают соответственно цвет текста документа, ссылок, которые не
были активизированы (документы, на которые они указывают, не посещались бро­
узером), цвет ссылок, которые были активизированы (документы, на которые они
указывают, просматривались с помощью данного броузера), и цвет выбранной ссыл­
ки. Выбранной считается та ссылка, на которую пользователь поместил курсор, на­
жал кнопку мыши, но еще не отпустил ее. Д о отпускания кнопки эта ссылка будет
отображаться цветом, указанном с помощью атрибута ALINK. Информация о про­
смотренных ранее документах хранится в списке предыстории броузера. Цвета за­
даются либо с помощью символьных имен, либо посредством шестнадцатеричного
представления. П р и шестнадцатеричном представлении цвет идентифицируется
последовательностью знаков, начинающейся с символа "#", за которым следует
шесть шестнадцатеричных ц и ф р . Две ц и ф р ы определяют интенсивность красного,
две — зеленого и две — синего цветов. Имена цветов и их шестнадцатеричные экви­
валенты представлены в табл. 1.1. Для систем, поддерживающих только 256 цветов,
определена палитра Color-Safe, определяющая подмножество из 216 цветов, не вы­
зывающих нежелательных эффектов на мониторе. Пример палитры Color-Safe при­
веден в документе h t t p : / / w w w . c i o c n p r o f . c o m / s a f e c o l o r A . h t m . Netscape и
Internet Explorer позволяют также использовать имена цветов, принятые в оконной
системе X I 1 . Примеры цветов X I 1 находятся в документе h t t p : / /www. z d n e t . com/
devhead/resources/tag_library/misc/xllnames.html.

Таблица 1.1 Имена цветов в HTML 4.0


Нмя цвета Шестнадцатеричный Имя цвета Шестнадцатеричный
эквивалент эквивалент
AQUA #OOFFFF NAVY #000080
BLACK #000000 OLIVE #808000
BLUE #OOOOFF PURPLE #800080
FUCHSIA #FFOOFF RED #FFOOOO
GRAY #808080 SILVER #COCOCO
GREEN #008000 TEAL #008080
LIME #OOFFOO WHITE #FFFFFF
MAROON #800000 YELLOW #FFFFOO

В большинстве броузеров по умолчанию используются ч е р н ы й цвет (BLACK, или


# 0 0 0 0 0 0 ) для отображения текста (атрибут TEXT), синий (BLUE либо более тем­
ный #0000ЕЕ) для ссылок (атрибут LINK), темно-пурпурный с примесью синего
(#551А8В) для ссылок, указывающих на документы, которые ранее были просмот­
рены в данном броузере (атрибут VLINK), и красный цвет (RED, или #FF0000) для
выбранных ссылок (атрибут ALINK).

TITLE
Атрибут TITLE позволяет организовать отображение подсказки в случае, если кур­
сор мыши приостанавливает свое движение в пределах документа. Д а н н ы й атри­
бут поддерживается Internet Explorer 4.0 и более новыми версиями. Netscape (по
крайней мере версия 4.7 и более старые) не поддерживает TITLE.
1.6. BODY — основная часть документа 53

ONCLICK, ONDBLCLICK, O N M O U S E D O W N , O N M O U S E U P ,
O N M O U S E M O V E , O N M O U S E O U T , ONKEYPRESS, ONKEY-DOWN,
ONLOAD, ONUNLOAD, ONFOCUS, ONBLUR, ONERROR, ONMOVE,
ONRESIZE, O N D R A G D R O P
Данные атрибуты определяют код JavaScript, который должен быть выполнен при
возникновении события, связанного с клавиатурой, мышью или изменением фокуса.

BGPROPERTIES
Данный атрибут поддерживается Internet Explorer. Если задано выражение
BGPROPERTIES = "FIXED", это означает, что ф о н о в о е изображение не должно
прокручиваться при просмотре страницы. Это свойство иногда называют
"водяными знаками" (watermark).
И наконец, при использовании каскадных листов стилей со всеми HTML-эле­
ментами могут использоваться атрибуты CLASS, ID и STYLE. Исключением являются
элементы BASE, BASEFONT (атрибут ID допустим), HEAD, HTML, МЕТА, PARAM (атрибут
ID допустим), SCRIPT, STYLE и TITLE. П р и рассмотрении конкретных HTML-эле­
ментов атрибуты, связанные с листами стилей, обсуждаться не будут. И н ф о р м а ц и ю об
этих атрибутах вы найдете в главе 5, посвященной каскадным листам стилей.

1.7. Резюме
Язык HTML— это основное средство для создания профессиональных Web-
страниц. Независимо от того, предназначена Web-страница для технической под­
держки некоторого продукта, отображения и н ф о р м а ц и и о котировке акций либо для
электронной коммерции, информация, содержащаяся в ней, представляет собой
HTML-документ. В данной главе описана общая структура HTML-дoк)^vIeнтa и рас­
смотрен процесс создания, проверки и размещения Web-страницы в Internet.
В каждом документе, соответствук:)щем спецификации HTML 4.0, содержится декла­
рация DOCTYPE, за которой следует элемент HTML, содержащий разделы HEAD и BODY. В
разделе HEAD всегда находится элемент TITLE, кроме того, HEAD может содержать
STYLE, МЕТА и др)^тие элементы, предоставляющие информацию об авторе, обеспечи­
вающие перенаправление либо содержащие описание документа. Информация, нахо­
дящаяся в элементе МЕТА, часто используется поисковыми серверами, которые поме­
щают сведения о документе в свои базы данных. Как правило, дескриптор <BODY> зада­
ется без атрибутов, однако при необходимости в него могут быть включены атрибуты,
которые задают фоновый цвет или фоновое изображение, а также JavaScript-команды,
которые должны быть выполнены при наступлении определенных событий.
В последующих главах будут рассмотрены вопросы оформления данных в составе
Web-страницы. В главе 2 обсуждаются основные т и п ы текстовых блоков, а в главе 3 —
элементы, которые мог)^' быть включены практически в любой блок. Глава 4 посвя­
щена созданию документов с использованием ф р е й м о в . Автор Web-страницы может
разбить ее на "ячейки" и разместить в каждой ячейке отдельный документ. Пользова­
тели, которые хотят задать особенности отображения различных элементов в окне
броузера, могут воспользоваться каскадными листами стилей, описанными в главе 5.
ЭЛЕМЕНТЫ
БЛОКОВОГО
УРОВНЯ HTML 4.0

В ЭТОЙ главе...

• Заголовки разделов.
• Основные типы абзацев.
• Маркированные и нумерованные списки.
• Таблицы.
• Горизонтальные разделительные линии.
• Типы выравнивания абзацев.
Й-1У\^^^

этой главе описываются основные т и п ы абзацев, или текстовых блоков, встре­

В чающихся в разделе BODY HTML-документа. Элементы блокового уровня опре­


деляют порядок ф о р м а т и р о в а н и я и отображения текстовых блоков в окне Web-
броузера. Элементы блокового уровня отличаются от элементов текстового уровня,
которые влияют лишь на внешний вид символов. В частности, в состав элемента бло­
кового уровня могут входить текст, элементы текстового уровня и другие элементы
блокового уровня, тогда как в элементах текстового уровня могут содержаться только
текст и вложенные элементы текстового уровня.
Согласно спецификации HTML 4.0, атрибуты, предназначенные для обработки со­
бытий посредством JavaScript-кода (ONCLICK, ONDBLCLICK, ONKEYDOWN, ONKEYPRESS,
ONKEYUP, ONMOUSEDOWN, ONMOUSEMOVE, ONMOUSEOUT, ONMOUSEOVER и ONMOUSEUP), мо­
гут содержаться во всех элементах за исключением APPLET, BASE, BASEFONT, BDO, BR,
FRAME, FRAMESET, HEAD, HTML, IFRAME, META, PARAM, SCRIPT, STYLE и TITLE. В данной
книге будут рассматриваться лишь общие принципы использования этих атрибутов в
HTML-документах. Особенности обработки событий посредством сценариев JavaScript
описаны в главе 24.
В соответствии со спецификацией каскадных листов стилей совместно со всеми
HTML-элементами (за исключением некоторых) могут использоваться атрибуты
CLASS, ID и STYLE. Д а н н ы е атрибуты н е п р и м е н и м ы к элементам BASE, BASEFONT
(атрибут ID может использоваться), HEAD, HTML, МЕТА, PARAM (атрибут ID может ис­
пользоваться), SCRIPT, STYLE и TITLE. П е р е ч и с л е н н ы е выше три атрибута будут рас­
сматриваться в главе 5, а при обсуждении отдельных HTML-элементов они упоми­
наться не будут.
Кроме того, в H T M L 4.0 для всех элементов, за исключением APPLET, BASE,
BASEFONT, BR, FRAME, FRAMESET, IFRAME, PARAM и SCRIPT, определены атриб)ты
LANG и DIR. Эти атрибуты были рассмотрены в главе 1 и в дальнейшем при обсужде­
нии HTML-элементов упоминаться не будут.
Наконец, атрибут TITLE, также рассмотренный в главе 1, допустим для всех эле­
ментов за исключением BASE, BASEFONT, HEAD, HTML, МЕТА, PARAM, SCRIPT и TITLE.
Данный атрибут также не упоминается при рассмотрении дескрипторов.
56 Глава 2 . Э л е м е н т ы блокового уровня HTML 4 . 0

2 . 1 . Заголовки
Элементы: <Н1 .. .> . . </Н1>
<Н2 .. .> . . </Н2>
<НЗ .. .> . . </НЗ>
<Н4 .. .> . . </Н4>
<Н5 . . .> . . </Н5>
<Нб . . .> . . </Нб>
Атрибут: ALIGN
Элементы Н1-Н6 предназначены для определения заголовков различных уровней
в составе документа. HI задает заголовок первого, или верхнего, уровня, Н2 — заголо­
вок второго уровня, НЗ — третьего уровня и т. д. Как правило, раздел BODY начинается
с заголовка первого уровня, содержащего тот же текст, что и элемент TITLE, находя­
щийся в разделе HEAD. В остальной части документа заголовки второго уровня (Н2)
используются для определения заголовков разделов, НЗ — для подразделов и т. д.
В большинстве броузеров заголовки отобрг1жаются полужирным шрифтом: HI —
наибольшего размера, а Нб— наименьшего. П р и использовании заголовков низших
уровней следует соблюдать осторожность, поскольку не исключена ситуация, при ко­
торой такой заголовок будет представлен ш р и ф т о м меньшего размера, чем обычный
текст абзаца. Текст, следующий за заголовком, оформляется как новый абзац. В отли­
чие от многих элементов, заголовок не содержит элементов блокового уровня и не
входит в состав других элементов, за исключением таблиц (TABLE) и ф о р м ввода
(FORM). В составе заголовка могут находиться элементы текстового уровня. Два заго­
ловка, показанные в листинге 2.1, созданы правильно, поскольку элементы текстово­
го уровня полностью содержатся внутри соответствующего заголовка. Один из них
задает отображение курсивом, а другой формирует гипертекстовую ссылку.

Листинг 2 . 1 . Заголовки, содержащие элементы текстового уровня

<Н2><1>Ап Italic Heading</I></H2>


<Н2><А NA]yiE="Section5">Section Five</A></H2>

Заголовки, представленные в листинге 2.2, заданы н е к о р р е к т н о , поскольку они


содержатся в составе элементов текстового уровня.

Листинг 2 . 2 . Некорректные заголовки, заданные в составе элементов


текстового уровня

<1><Н2>Ап I t a l i c Heading</H2></I>
<А NAME="Section5"><H2>Section Five</H2></A>

Данные примеры показывают, как важно использовать программы проверки син­


таксиса документов. Поскольку многие (но не обязательно все) броузеры отобразят
оба документа одинаково, у автора может сложиться ошибочное мнение, что доку­
мент составлен корректно и будет иметь тот же вид в окне любой клиент-программы
Web, поддерживающей HTML 4.0.
2.1. Заголовки 57

ALIGN
По умолчанию к заголовкам применяется выравнивание по левому краю, однако при
необходимости они могут быть расположены по центру либо выровнены по правому
краю документа. Тип выравнивания задается с помощью атрибута ALIGN, допусти­
мыми значениями которого являются LEFT, RIGHT, CENTER и JUSTIFY. Если задано
значение JUSTIFY, то заголовки, длина которых превышает ширину окна броузера,
располагаются так, что их левая и правая границы совпадают с границами окна. Тип
выравнивания по умолчанию может быть изменен посредством дескриптора DIV,
который будет рассмотрен далее в этой главе. В спецификации HTML 4.0 не реко­
мендуется использовать атрибут ALIGN при создании заголовков. Необходимое вы­
равнивание желательно выполнять с помощью каскадных листов стилей.
П р и м е р ы заголовков различных уровней приведены в листинге 2.3. Заголовок
третьего уровня отобрг1жается с подчеркиванием. На рис. 2.1 показан внешний вид
этого документа в окне броузера Internet Explorer 5.0, выполняющегося в среде
Windows 2000 Professional.

^ч^l•нlNl^^•l-f^'^У4•l'llн^•'гl^'l^llll1u'•^n'^f'l——
^m\
:^ - -^r Jj}^ 'й^ ^ -y^-yj •

1 Samples of the six heading types: J


Level-l (HI) '1

Level-2 (H2) "


Level-3 ШЗ)

L c v d - 4 (H4)

Level-5(H5)
'
Level-6(H6)

J
1
^gjDorte 1^ My CoriipUtM

Рис. 2.1. Внешний вид документа Document-Headings .html

Листинг 2 . 3 . Исходный текст д о к у м е н т а D o c i o m e n t - H e a d i n g s . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Document H e a d i n g s < / T I T L E >
</HEAD>
<BODY>
Samples of t h e s i x h e a d i n g t y p e s :
< H l > L e v e l - l (H1)</H1>
<H2 ALIGN="CENTER">Level-2 (H2)</H2>
<H3><U>Level-3 (H3)</U></H3>
<H4 ALIGN="RIGHT">Level-4 (H4)</H4>
<H5>Level-5 (H5)</H5>
58 Глава 2. Элементы блокового уровня HTML 4.0

<H6>Level-6 (Нб)</Н6>
</BODY>
</HTML>

2.2. Основные элементы, предназначенные


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

Основной абзац
Элемент: <Р . . . > . . . </Р>(закрь1ваю1ций дескриптор может отсутствовать)
Атрибут: ALIGN
Элемент Р определяет основной абзац — фрагмент текста, при отображении кото­
рого броузер оставляет пустое пространство небольшого размера выше и ниже абза­
ца. Несмотря на то что Р представляет собой контейнер, закрывающий дескриптор
указывать не обязательно. Если дескриптор < / Р > пропуш^ен, окончание абзаца опре­
деляется по началу нового элемента блокового уровня. Если текст расположен за пре­
делами всех элементов блокового уровня, считается, что он содержится в элементе Р.
Учитывая эти особенности, многие авторы используют Р как разделитель, хотя на са­
мом деле он является контейнером. В листинге 2.4 показаны два варианта использо­
вания дескриптора основного абзаца. В первом варианте (а) окончание абзаца указы­
вается явно, а во втором варианте (б) закрывающий дескриптор пропущен.
Л и ш н и е пробелы в составе основного абзаца игнорируются, а текст располагается
в соответствии с размерами окна. Несколько последовательно расположенных деск­
р и п т о р о в <Р> не приводят к увеличению пустого пространства над абзацем. Каскад­
ные листы стилей (см, главу 5) позволяют непосредственно задавать верхнюю и ниж­
нюю границы абзаца. Если при создании документа листы стилей не используются,
для включения пустых строк можно использовать элемент BR, который будет обсуж­
даться в главе 3, либо элемент PRE, рассматриваемый далее в этой главе.

Листинг 2.4. Явное (а) и неявное (6) определение границ абзаца

(а)
<BODY>
<Р>
Paragraph
</Р>
<Р>
Paragraph
</Р>
2 . 2 . Основные э л е м е н т ы . . . 59

<р>
Paragraph 3
</Р>
</BODY>
(б)
<BODY>
Paragraph 1
<Р>
Paragraph 2
<Р>
Paragraph 3
</BODY>

Внимание!

Включение нескольких последовательно расположенных дескрип­


торов <р> не приведет к появлению пустых строк.

ALIGN
Атрибут ALIGN элемента Р применяется так же, как и соответствующий атрибут
заголовков. Подобно заголовкам, основной абзац по умолчанию выравнивается по
левой границе, но посредством атрибута ALIGN можно задать выравнивание по
центру либо по правой границе. Допустимыми значениями атрибута ALIGN явля­
ются LEFT, RIGHT, CENTER и JUSTIFY. Если задано значение JUSTIFY, то содер­
жимое абзаца, состоящего из нескольких строк, распределяется по всей ширине
окна броузера. Тип выравнивания по умолчанию можно изменить с помощью де­
скриптора DIV (см. раздел 2.6) или каскадных листов стилей (см. главу 5). Как и для
заголовков, атрибут ALIGN не рекомендован для применения. Это сделано для то­
го, чтобы стимулировать использование листов стилей.

Абзацы с сохранением пробелов


Элемент: <PRE>... </PRE>
Атрибут: WIDTH
Обычно лишние пробелы (а также символы возврата каретки и перевода строки),
содержащиеся в HTML-документе, не отображаются в окне броузера. Элемент PRE
определяет заранее отформатированный абзац, при отображении которого сохра­
няются пробелы, заданные в исходном тексте документа, и используется моноши­
ринный шрифт. Элемент PRE часто применяется для включения в состав документа
фрагментов программ. В состав PRE не должны входить элементы, изменяющие раз­
мер шрифта. Символы "<" и "&" в коде программы, представленном с помощью эле­
мента PRE, мог)т интерпретироваться как HTML-элементы разметки. Например, об­
рабатывая выражение (а < b && с < d), броузер может принять последователь­
ность символов "< Ь" за начало элемента <В>. Поэтому в указанном выражении
следует заменить символ "<" последовательностью "&lt;", а символ "&"— последова­
тельностью "&атр;".
60 Глава 2 . Элементы блокового уровня HTML 4.0

Для представления любого из символов набора ISO 8859-1 (Latin-1) может использо­
ваться последовательность "&#л;л:л^', где ххх— это десятичное представление символа в на­
боре ISO Latin-1. Так, например, если в документе встретится последовательность &#1б9,
на экране будет отображаться символ ©. Кроме того, для многих символов применяются
мнемонические обозначения, например символ © может быть задан как " &сору;". Полный
перечень символов Latin-1 и их мнемонических обозначений находится по адресу
h t t p : / / w w w . h t m l h e l p . c o m / r e f e r e n c e / h t m l 4 0 / e n t i t i e s / l a t i n l .html.
В табл. 2.1 перечислены некоторые обозначения, поддерживаемые всеми броузе­
рами H T M L 4.0. Эти обозначения могут быть использованы в составе элемента TITLE
и при ф о р м и р о в а н и и надписи на кнопке SUBMIT, где элементы разметки запрещены.

Таблица 2 . 1 . Специальные символы HTML

Символ Мнемоническое обозначение


< &lt;
> &gt;
& &amp;
&quot;
Н е р а з р ы в н ы й пробел Snbsp;

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

Цитирование с отступом
Элемент: <BLOCKQUOTE> ... </BLOCKQUOTE>
Атрибут: CITE
Элемент BLOCKQUOTE представляет большой фрагмент цитируемого текста и ото­
бражает его с отступом. Большинство броузеров формируют отступ как от левой, так и
от правой границы окна. Однако такие отступы не считаются обязательными, поэтому
авторы часто избегают включать BLOCKQUOTE в свои документы и применяют этот эле­
мент в основном в сетях intranet, где известно, в каких броузерах будут отображаться
Web-страницы. Вместо BLOCKQUOTE можно использовать соответствующие возможно­
сти листов стилей, а также таблиц)^ без обрамления. Атрибут CITE, не обрабатываемый
броузером, позволяет указывать URL документа, текст которого цитируется.

Методика профессионалов

В HTML 4.0 не определены элементы с отступом только от левой или


от правой границы. Для вывода фрагментов текста с отступом авто­
ры часто используют элементы BLOCKQUOTE ИЛИ таблицу без обрам­
ления, выровненную по центру окна.
2.3. Нумерованные, маркированные списки... 61

Адрес
Элемент: <ADDRESS>... </ADDRESS>
Атрибуты: отсутствуют
Элемент ADDRESS позволяет включать в документ и н ф о р м а ц и ю об авторе. О б ы ч н о
эти данные отображаются в верхней или в н и ж н е й части документа; многие броузеры
выводят их курсивом. Как и в других блоковых элементах (за исключением PRE), бро­
узер располагает текст по ширине окна. Ч т о б ы явным образом включить в текст раз­
рыв строки, надо использовать элемент BR.

2.3. Нумерованные, маркированные списки


и списки определений
HTML позволяет автору создавать нумерованные списки (0L— ordered list), марки­
рованные списки (UL — u n o r d e r e d list) и списки определений (DL— definition list). Бро­
узеры также поддерживают списки MENU и DIR, но поскольку аналогичные возможности
реализованы современными средствами, мы не рассматриваем эти элементы.

Нумерованные списки
Элемент: <OL ...>... </OL>
Атрибуты: TYPE, START, COMPACT
С помощью элемента 0L создаются нумерованные списки. Элемент LI (list item—
пункт списка) отмечает отдельные пункты нумерованного списка, которые могут со­
держать элементы блокового уровня (например, таблицы и другие списки), за исклю­
чением заголовков (HI — Нб) и элементов ADDRESS. П р и м е р простого нумерованного
списка приведен в листинге 2.5. На рис. 2.2 показано, как этот код отображается в
броузере Netscape 4.0, который выполняется на рабочей станции Sun/Solaris.

Листинг 2 . 5 . П р о с т о й н у м е р о в а н н ы й с п и с о к

А sample l i s t :
<0L>
< L I > L i s t I t e m One
< L I > L i s t I t e m Two
<LI>List Item Three
</0L>

A sample list:

1, List Item One


Z. List i t e m i w o p^^^ 2.2. Порядковые номера в нумерованном
? Т Wf Tfpm Three к > к
о. i-i^LiLciu 11UCC списке генерируются автоматически
62 Глава 2. Элементы блокового уровня HTML 4.0

В состав открывающего дескриптора 0L могут быть включены необязательные ат­


рибуты TYPE, START и COMPACT.

TYPE
Атрибут TYPE определяет стиль нумерованного списка (тип нумерации). Допусти­
мые значения данного атрибута приведены в табл. 2.2. Официально атрибут TYPE не
рекомендован для использования, однако многие авторы находят его очень удобным
и продолжают применять.

Таблица 2.2. Значения атрибута TYPE нумерованного списка


Значение Описание
1 Нумерация арабскими цифрами (1, 2, 3 и т.д.); используется по
умолчанию
А Нумерация прописными буквами (А, В, С и т.д.)
а Нумерация строчными буквами (а, Ь, с и т.д.)
I Нумерация римскими цифрами, которые отображаются прописны­
ми латинскими буквами (I, II, III, IV и т.д.)
Нумерация римскими цифрами, которые отображаются строчными
латинскими буквами (i, ii, iii, iv и т.д.)

START
Атриб)т START также не рекомендован для применения. Его значением является
целое число, определяющее начало нумерации. Данный атрибут может быть ис­
пользован с любым типом нумерации. В элементе 0L не предусмотрена возмож­
ность непосредственно задавать префикс, отображаемый в начале каждого пункта.

COMPACT
Атрибут COMPACT указывает на то, что спи­ I. Headings
сок должен отображаться компактно (т.е. не II. Basic Text Sections
занимать лишнее пространство). Этот атри­ m Lists
бут не рекомендован для применения и ис­ A. Ordered
пользуется крайне редко. 1. The OL tag
a TYPE
В листинге 2.6 представлен более сложный b, START
нумерованный список. На рис. 2.3 показано, с COMPACT.
как этот список выглядит в окне броузера. За­ 2. The LI tag
метьте, что вложенные списки отображаются с B. Unordered
отступом и их нумерация начинается с 1, неза­ 1. The UL tag
висимо от нумерации включающего списка. 2. The LI tag
С Definition
1. The DL tag
2. The DT tag
Рис. 2.3. Вложенные списки 3. The DD tag
отображаются с отступом IV. Miscellaneous
2.3. Нумерованные, маркированные списки... 63

Листинг 2.6. Вложенные нумерованные списки

<0L TYPE="I">
<LI>Headings
<LI>Basic Text Sections
<LI>Lists
<0L TYPE="A">
<LI>Ordered
<0L TYPE="1">
<LI>The OL tag
<0L TYPE="a">
<LI>TYPE
<LI>START
<LI>COMPACT
</0L>
<LI>The LI tag
</0L>
<LI>Unordered
<0L TYPE="1">
<LI>The UL tag
<LI>The LI tag
</0L>
<LI>Definition
<0L TYPE="1">
<LI>The DL tag
<LI>The DT tag
<LI>The DD tag
</0L>
</0L>
<LI>Miscellaneous
</0L>

Элемент: <LI ...> ... </LI> (закрывающий дескриптор может


отсутствовать)
Атрибуты: VALUE, ТУРЕ (при использовании в составе 0L)
Атрибуты LI зависят от типа списка, в составе которого применяется данный де­
скриптор. Для маркированного списка открывающий дескриптор <Ы> может содер­
жать атрибуты VALUE и ТУРЕ. Элемент LI является контейнером, но закрывающий
дескриптор часто не указывается.

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

TYPE
В составе открывающего дескриптора <LI> может также указываться атрибут
ТУРЕ, который позволяет изменять тип нумерации в пределах списка. Необходи­
мость в применении этого атрибута возникает крайне редко.
64 Глава 2. Элементы блокового уровня HTML 4.0

Маркированные списки
Элемент: <UL ...>.., < / U L >
Атрибуты: TYPE, COMPACT
Элемент UL создает маркированный список. Пункты списка формируются с помо­
щью дескриптора < Ы > . П р и м е р простого маркированного списка приведен в лис­
тинге 2.4. На рис. 2.7 показано, как этот список отображается в окне броузера
Netscape 4.0, выполняющегося на рабочей станции Sun/Solaris.

Листинг 2.7. Простой маркированный список

А sample l i s t :
<UL>
< L I > L i s t I t e m One
< L I > L i s t I t e m Two
<LI>List Item Three
</UL>

A sample list:

• List Item One


• List I t e m iV/Q p^^ 2.4. Элемент UL автоматически создает маркер для каждого
Ш * ^^^^ i t e m i i i r e e пункта списка, сформированного с помощью дескриптора < ы >

В состав открывающего дескриптора <UL> могут быть включены необязательные


атрибуты TYPE и COMPACT.

TYPE
Атрибут TYPE задает т и п маркера. Допустимые значения TYPE приведены в
табл. 2.3. Ч т о б ы стимулировать использование листов стилей, данный атрибут не
рекомендован для применения.

Таблица 2.3. Значения атрибута TYPE

Значение Описание
DISC З а к р а ш е н н ы й круг. Часто используется по умолчанию для списков,
не являющихся вложенными
CIRCLE Незакрашенный круг. Обычно используется по умолчанию для вло­
ж е н н ы х списков
SQUARE Квадрат. В зависимости от броузера, он может быть закрашен либо
не закрашен
2.3. Нумерованные, маркированные списки... 65

COMPACT
Как и TYPE, атрибут COMPACT не рекомендован для использования. Этот атрибут
указывает на то, что список должен отображаться компактно, и применяется дос­
т а т о ч н о редко.

Элемент: < Ы . . .> . . . </Ы>(закрывающий дескриптор мелеет


отсутствовать)
Атрибут: TYPE (при использовании в составе UL)
В зависимости от типа списка, в состав открывающего дескриптора LI могут быть
включены различные атрибуты. Для маркированного списка используется только ат­
рибут TYPE. Элемент LI представляет собой контейнер, но закрывающий дескриптор
часто не указывается.

TYPE
Атрибут TYPE (не рекомендован для применения) задает тип маркера для кон­
кретного пункта списка. Для атрибута TYPE допустимы те же значения, что и для
соответствующего атрибута дескриптора UL (т.е. DISC, CIRCLE и SQUARE).

Списки определений
Э л е м е н т : <DL . . .> ... </DL>
Атрибут: COMPACT

Элемент: <DT> . . . </DT> (закрывающий дескриптор молсет


отсутствовать)
Атрибуты: отсутствуют

Элемент: <DD> . . . </DD> (закрывающий дескриптор молсет


отсутствовать)
Атрибуты: отсутствуют
Элемент DL содержит пункты, отображаемые как с отступом, так и без отступа.
Пункты списка не помечаются ни номерами, ни маркерами. Т е р м и н ы определений
(DT— definition terms) выравниваются по левой границе окна, а описания определе­
ний (DD— definition description) отображаются с отступом. П р и м е р простого списка
определений приведен в листинге 2.8. Внешний вид этого списка в окне броузера
представлен на рис. 2.5. В состав открывающего дескриптора DL может быть включен
необязательный атрибут COMPACT, но в настоящее время он применяется достаточно
редко. Формально элементы DT и DD являются контейнерами, но соответствующие
закрывающие дескрипторы часто не указываются.
66 Глава 2. Элементы блокового уровня HTML 4.0

Листинг 2.8. Простой список определений

<DL>
<DT>Term One
<DD>The definition of term number one.
<DT>Term Two
<DD>The definition of term number two.
</DL>

Term One
The definition of term
number one.
Term Two
The aenniuon of term p^iQ^ 2.5. в списке определений элементы DD обычно
number two. отображаются с отступом

В состав DD могут входить элементы блокового уровня, за исключением заголовков и


адресов. Элементы DD могут присутствовать в списке DL без соответствующих элемен­
тов DT. Элемент блокового уровня DD отображается с отступом от левой, но не от пра­
вой границы окна. В предыдущих версиях HTML для отображения фрагментов текста с
отступом от левой границы окна использовались элементы DD (а также таблицы без об­
рамления с пустым левым столбцом фиксированной ширины). В HTML 4.0 гораздо
ббльшую гибкость при отображении с отступом обеспечивают каскадные листы стилей.

2.4. Таблицы
HTML-таблицы используются не только традиционным способом (т.е. для пред­
ставления данных в виде двухмерной таблицы), они также применяются для управле­
ния расположением и группировки элементов. В состав таблиц могут входить изо­
бражения, фрагменты текста, включая списки, и даже другие таблицы. Автор Web-
страницы имеет возможность запретить отображение обрамления вокруг таблицы и
разграничительных л и н и й между ячейками, в результате содержимое таблицы выгля­
дит как выровненные фрагменты данных. Создание сложных таблиц вручную — дли­
тельная и р)а^инная работа, при выполнении которой легко допустить ошибку. Боль­
шую помощь при создании Web-страниц оказывают HTML-редакторы, предостав­
ляющие визуальные средства для ф о р м и р о в а н и я и заполнения таблиц. Особенно
полезны такие редакторы, которые позволяют редактировать исходный HTML-код и
включать в него атрибуты, не предусмотренные в графическом и н т е р ф е й с е
(например, атрибут BGCOLOR).

Структура таблицы
HTML-таблица создается с помощью элемента TABLE, который содержит необяза­
тельный элемент CAPTION и описания строк, с ф о р м и р о в а н н ы е с помощью элементов
TR. В состав каждой строки таблицы могут входить элементы ТН (table heading — заго­
ловок таблицы) и элементы TD (table data— данные таблицы). В составе элемента ТН
2.4. Таблицы 67

информация по умолчанию отображается полужирным ш р и ф т о м и выравнивается по


центру. Данные в элементе TD выводятся обычным ш р и ф т о м и выравниваются по ле­
вому краю. П р и м е р простой таблицы представлен в листинге 2.9. Н а рис. 2.6 показан
внешний вид этой таблицы на экране броузера. Закрывающие дескрипторы </TR>,
</ТН>и</ТО> необязательны, однако помогают проследить структуру таблицы.

Листинг 2 . 9 . Простая HTML-таблица

<TABLE B0RDER=1>
<CAPTION>Table Caption</CAPTION>
<TR><TH>Headingl</TH> <TH>Heading2</TH></TR>
<TR><TD>Rowl C o l l Data</TD><TD>Rowl Col2 Data</TD></TR>
<TR><TD>Row2 C o l l Data</TD><TD>Row2 Col2 Data</TD></TR>
<TR><TD>Row3 C o l l Data</TD><TD>Row3 Col2 Data</TD></TR>
</TABLE>

||R<>wl Cott Data jRqwl CoE Data


I |й[(йй Coll Й а ^ 1 1 ^ ^ ОЙ
Рис. 2.6. Так ВЫГЛЯДИТ таблица, код которой
feowi СЩ Data [Roi^ (ЗоЁ Bata приведен в листинге 2.9

Помимо основных элементов, определяющих структуру таблицы, в спецификации


HTML 4.0 предусмотрены новые средства, позволяющие группировать строки
(THEAD, TBODY и TFOOT) и столбцы (COLGROUP и COL). Эти новые элементы обеспечи­
вают дополнительный контроль ф о р м а т а таблицы. Группирование строк и столбцов
мы рассмотрим после обсуждения основных средств построения таблицы.

Элемент: <TABLE>... </TABLE>


Атрибуты: BORDER, ALIGN, WIDTH, CELLSPACING, CELLPADDING, FRAME, RULES, SUMMARY,
BGCOLOR, BORDERCOLOR (нестандартный), BORDERCOLORDARK (нестандартный),
BORDERCOLORLIGHT (нестандартный), BACKGROUND (нестандартный)
Если в открывающем дескрипторе <TABLE> не указаны атрибуты, по умолчанию
создается таблица без обрамления, выровненная по левому краю. Дополнительные
возможности по управлению внешним видом таблицы предоставляют следующие ат­
рибуты.

ALIGN
Атриб)т ALIGN (чтобы стимулировать использование листов стилей, он не реко­
мендован для применения) определяет тип выравнивания всей таблицы по гори­
зонтали. Допустимыми значениями являются LEFT, RIGHT и CENTER, значение
LEFT принимается по умолчанию. Заметьте, что текст, непосредственно следую­
щий в документе за таблицей, в ы р о в н е н н о й вправо, будет отображаться слева от
нее. Чтобы текст выводился под таблицей, надо использовать выражение <BR
CLEAR="ALL">. Подробно элемент BR будет рассматриваться в главе 3.
68 Глава 2 . Э л е м е н т ы блокового уровня H T M L 4 . 0

BORDER
В основном атрибут BORDER предназначен для управления шириной обрамления,
отображаемого вокруг таблицы. Н о если данный атрибут будет установлен равным
нулю (значение по умолчанию), разделительные линии между ячейками таблицы
также не будут выводиться. Размеры, указанные с помощью атрибута BORDER, до­
бавляются к размерам пустого пространства вокруг каждой ячейки (атрибут
CELLSPACING). Н е к о т о р ы е броузеры интерпретируют выражение <TABLE
BORDER> как <TABLE B0RDER=1>. Подобные выражения не следует использовать в
составе Web-страниц, поскольку они мешают проверять документы с помощью
специализированных программ.

На з а м е т к у

Если значение атрибута BORDER не равно нулю, толщина обрамления


вычисляется как сумма значений BORDER И CELLSPACING.

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

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

FRAME
Атрибут FRAME введен в HTML 4.0 и определяет, какая часть внешнего обрамле­
ния должна отображаться в окне броузера. Если данный атрибут не указан, все че­
т ы р е части обрамления выводятся на экран. Допустимыми значениями FRAME яв­
ляются BORDER, или BOX (отображаются все части обрамления), VOID (обрамление
не отображается), ABOVE (отображается верхняя л и н и я ) , BELOW (отображается
нижняя л и н и я ) , HSIDES (отображаются верхняя и нижняя линии, хотя по имени
атрибута можно предположить другое действие), VSIDES (отображаются левая и
правая л и н и и ) , LHS (отображается левая линия) и RHS (отображается правая ли­
ния). П р о с т о й п р и м е р использования атрибута FRAME приведен в листинге 2.10.
На рис. 2.7 показано, как этот пример выглядит на экране броузера. Атрибут
FRAME не поддерживается Netscape, по крайней мере он не поддерживался в
Netscape 4.7 и более ранних версиях данного броузера.
2 . 4 . Таблицы 69

Листинг 2.10. T i c T a c T o e . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>2000 World C h a m p i o n s h i p < / T I T L E >
</HEAD>
<BODY>
<H2 ALIGN="CENTER">2000 World C h a m p i o n s h i p < / H 2 >
F i n a l r e s u l t i n t h e 2000 w o r l d t i c - t a c - t o e c h a m p i o n s h i p .
Deep Green i s "X", B a r r y K a s p a r o v i s " 0 " .
<TABLE ALIGN="CENTER" B0RDER=1 FRAME="VOID">
<TR><TH>X<TH>0<TH>X
<TR><TH>X<TH>0<TH>X
<TR><TH>0<TH>X<TH>0
</TABLE>
</BODY>
</HTML>

¥т^тштт*мт&жМШ1Шшшшт
||;1в||Ш1|Я||||Ж
|1иш11вай1Й11в& ^ '^7
2000 World Championship
Ш Щ
1 Final result in the 2000 world tic-tac-toe championsh^. Deep Green i5"X". '
1 Barry Kasparov is "0".
1 X jO X
X |0 X
OJX(0

ш^¥шт^^-^ш1шшшшшшшщшшш
Рис. 2.7. Таблица с отключенным внешним обрамлением

Внимание!

Броузер Netscape не поддерживает атрибут FRAME элемента TABLE.

RULES
Новый атрибут H T M L 4.0, поддерживаемый Internet Explorer, но не поддержи­
ваемый Netscape, определяет, какие из внутренних разделительных л и н и й табли­
цы должны быть отображены на экране. Допустимыми значениями атрибута
RULES являются NONE, ROWS, COLS, ALL и GROUPS.

Внимание!

Броузер Netscape не поддерживает атрибут RULES элемента TABLE.


70 Глава 2. Элементы блокового уровня HTML 4.0

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

WIDTH
Атрибут WIDTH задает ширину таблицы в пикселях (<TABLE WIDTH=250>) либо в
процентах от ш и р и н ы окна броузера (<TABLE WIDTH="75%">). П р и использова­
нии абсолютных размеров соблюдайте осторожность, так как вы не можете пред­
положить, какой будет ширина окна при просмотре документа пользователем. За­
давая ширину в процентах, следует поместить значение в двойные кавычки. П о
умолчанию ширина таблицы определяется исходя из размеров ее ячеек.

BGCOLOR
Цвет таблицы (либо ее отдельных строк или ячеек) существенно влияет на внеш­
ний вид Web-страницы. Для определения цвета таблицы можно использовать ат­
рибут BGCOLOR дескриптора <TABLE> (либо соответствующие средства листов
стилей, описанные в главе 5). Преимущество листов стилей состоит в том, что ес­
ли вы захотите изменить цвет всех таблиц в составе Web-страницы, вам достаточ­
но внести лишь незначительные изменения в код документа.

BORDERCOLOR, BORDERCOLORDARK, BORDERCOLORLIGHT


Эти нестандартные атрибуты поддерживаются только Internet Explorer 4.0 и более
поздними версиями. Они определяют цвета обрамления таблицы. BORDERCOLOR
задает основной цвет, а BORDERCOLORDARK и BORDERCOLORLIGHT — цвета для ото­
бражения теней при имитации трехмерного представления. Данные атрибуты
учитываются только в том случае, если значение BORDER не равно нулю.

BACKGROUND
BACKGROUND— атрибут, специфический для Internet Explorer, он задает файл, со­
держащий изображение, которое должно выводиться в качестве ф о н а таблицы.
Если отключить отображение обрамления, данный атрибут можно использовать
для вывода текста на ф о н е изображения, однако данная возможность не поддер­
живается другими броузерами. Листы стилей позволяют реализовать такое ото­
бражение в любом из новых броузеров. Учитывая это, мы не рекомендуем вам
применять атрибут BACKGROUND даже в тех случаях, когда Web-страницы предна­
значены для внутренней сети, в которой используются броузеры Internet Explorer.

Элемент: <CAPTION>... </CAPTION>


Атрибуты: ALIGN
Элемент CAPTION располагается между открывающим и закрывающим дескрипто­
рами <TABLE> и размещает заголовок или название таблицы над (ALIGN="TOP") либо
под ней (ALIGN= "BOTTOM"). По умолчанию принимается значение ТОР. Многие авто­
ры Web-страниц избегают применять атрибут CAPTION и создают заголовки таблиц с
помощью других HTML-элементов, которые предоставляют большую свободу в выбо­
ре формата.
2.4. Таблицы 71

Определение строк таблицы


Элемент: <TR ...> ... </TR> (закрывающий дескриптор мелеет
отсутствовать)
Атрибуты: ALIGN, VALIGN, BGCOLOR, BORDERCOLOR (нестандартный), BORDERCOLORDARK
(нестандартный), BORDERCOLORLIGHT (нестандартный), CHAR, CHAROFF
Элемент TR определяет строку таблицы. В составе строки содержатся элементы ТН
или TD.

ALIGN
Дескриптор ALIGN (допустимые значения: LEFT, RIGHT, CENTER, JUSTIFY и CHAR)
задает тип горизонтального выравнивания содержимого ячеек в строке таблицы.
По умолчанию принимается значение LEFT. Значения JUSTIFY и CHAR не поддер­
живаются ни Netscape, ни Internet Explorer.

VALIGN
Дескриптор VALIGN (допустимые значения: ТОР, MIDDLE, BOTTOM и BASELINE) за­
дает тип вертикального выравнивания содержимого ячеек в строке таблицы. Если
указано значение BASELINE, первые строки каждой ячейки выравниваются по од­
ной базовой линии. П о умолчанию принимается значение MIDDLE.

BGCOLOR
Атрибут BGCOLOR задает цвет ф о н а для строки таблицы. Цвет для строки замещает
цвет, заданный для всей таблицы с помощью атрибута BGCOLOR элемента TABLE.
Цвет для строки, в свою очередь, замещается цветом, заданным для отдельной
ячейки с помощью атрибута BGCOLOR элемента ТН или TD. Это дает возможность
выделить цветом одну или несколько ячеек таблицы. Так, например, в листинге
2.11 приведен п р и м е р таблицы, для которой в заголовках столбцов текст отобра­
жается белым цветом на черном ф о н е , а в остальных ячейках выводится черный
текст на светло-сером ф о н е . Внешний вид таблицы показан на рис. 2.8. Таблицы
такого типа предпочитают большинство пользователей.

Листинг 2 . 1 1 . BG-Colors. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>WWW S t a n d a r d s < / T I T L E >
</HEAD>
<BODY BGCOLOR="WHITE">
<H1 ALIGN="CENTER">WWW S t a n d a r d s < / H l >

<TABLE B0RDER=1 BGCOLOR="#EEEEEE">


<TR BGCOLOR="BLACK">
<TH><FONT COLOR="WHITE">Standard</FONT>
<TH><FONT COLOR="WHITE">Obsolete Version</FONT>
<TH><FONT COLOR="WHITE">Most W i d e l y S u p p o r t e d
72 Глава 2. Элементы блокового уровня HTML 4.0

Version</FONT>
<TH><FONT COLOR="WHITE">Upcoming Version</FONT>
<TR><TD>HTML
<TD>3.2
<TD>4.0
<TD>XHTML
<TR><TD>HTTP
<TD>1.0
<TD>1.1
<TD>1.2
</TABLE>

</BODY>
</HTML>

ШШШЕШШШВШ

И11^И!МКД!1И
WWW Standards

jHIML 3.2 40 XKIML


ЙГГР "'.Го' 11 " ' 1.2 "

,Ooci«w*,0«w

Рис. 2.8. Атрибут BGCOLOR позволяет изменить


внешний вид таблицы

BORDERCOLOR, BORDERCOLORDARK, BORDERCOLORLIGHT


Данные атрибуты, поддерживаемые только Internet Explorer, задают цвет раздели­
тельных линий в строке. BORDERCOLOR задает основной цвет, а BORDERCOLORDARK и
BORDERCOLORLIGHT— цвета для отображения теней при имитации трехмерного
представления. Данные атрибуты учитываются только в том случае, если значение
BORDER элемента TABLE не равно нулю.
Спецификация HTML 4.0 определяет для элементов TR, ТН, TD, THEAD, TBODY и
TFOOT атрибуты CHAR и CHAROFF. Если задан атрибут CHAR, то броузер выравнивает со­
держимое каждой ячейки в строке так, что относительные расположения указанных
символов (например, десятичных точек) остаются одинаковыми. Атрибут CHAROFF за­
дает смещение до первого выравниваемого символа. Ни Netscape, ни Internet Explorer
не поддерживают эти атрибуты, поэтому мы не будем обсуждать их при рассмотрении
соответствующих элементов.
2 . 4 . Таблицы 73

Ячейки заголовков и ячейки данных


Элементы: <ТН . . . > . . . </ТН> (закрывающий дескриптор молсет
отсутствовать)
<TD . . . > . . . </TD> (закрыван>щий дескриптор молсет
отсутствовать)
Атрибуты: COLSPAN, ROWSPAN, ALIGN, VALIGN, WIDTH, HEIGHT, NOWRAP, BGCOLOR,
BACKGROUND (нестандартный), BORDERCOLOR (нестандартный), BORDERCOLORDARK
(нестандартный), BORDERCOLORLIGHT (нестандартный), ABBR, AXIS, HEADERS, SCOPE

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

Листинг 2.12. Пример заголовка, распространяющегося на два столбца

<TABLE BORDER-1>
<TR><TH COLSPAN=2>Col 1&2 H e a d i n g
<ТН>Со13 H e a d i n g
<TR><TD>Coll Data
<TD>Col2 D a t a
<TD>Col3 D a t a
</TABLE>

iteidingi iiiee
цЩШЩ^^^^Ш
Co! 1&2 Heading CoB Holding ^ , „ ,0.0^41.^°^^°^^^^^
, * , *> Data for Row 1&2, Coll —™.__
CoU Data Col2 Data CoB Data ' |J^ow2 Col2 Data
(a) (6)
Рис. 2.9. (a) Расширение ячейки на несколько столбцов (пример кода приведен в листинге
2.12); (б) расширение ячейки на несколько строк (пример кода приведен в листинге 2.13)

ROWSPAN
Атрибут ROWSPAN определяет ячейку заголовка или данных, которая расширяется
на несколько строк. П р и м е р использования ROWSPAN приведен в листинге 2.13,
а внешний вид таблицы — на рис. 2.9,6.

Листинг 2.13. Ячейка данных, расширяющаяся на две строки

<TABLE B0RDER=1>
<TR><TH>Headingl<TH>Heading2
<TR><TD R0WSPAN=2>Data f o r Row 1&2, Coll
<TD>Rowl C0I2 D a t a
<TR><TD>Row2 C0I2 D a t a
</TABLE>
74 Глава 2. Элементы блокового уровня HTML 4.0

ALIGNHVALIGN
Атрибут ALIGN (допустимые значения: LEFT, RIGHT, CENTER, JUSTIFY и CHAR) за­
дает горизонтальное выравнивание данных в ячейке таблицы. П о умолчанию для
элементов TD принимается значение LEFT, а для элементов ТН — CENTER. Деск­
р и п т о р VALIGN (допустимые значения: ТОР, MIDDLE, BOTTOM и BASELINE) задает
тип вертикального выравнивания содержимого ячейки. П о умолчанию принима­
ется значение MIDDLE. З н а ч е н и я CHAR и BASELINE описаны при рассмотрении
элемента TR.

WIDTH и HEIGHT
Если атрибуты WIDTH и HEIGHT не указаны, размеры ячеек устанавливаются бро­
узером, исходя из размеров окна и данных, содержащихся в ячейках. Атрибуты
WIDTH и HEIGHT позволяют явно задавать размеры ячейки в пикселях. Устанавли­
вая размеры ячеек явным образом, следует соблюдать осторожность. Если разме­
ры окна броузера на компьютере пользователя будут не такими, как предполагает
автор документа, ф о р м а т таблицы нарушится. Иногда ячейки с фиксированными
размерами используются для создания панелей инструментов с пиктограммами.
П р и этом размеры пиктограмм должны быть заранее известны и хотя бы одна
ячейка в строке должна допускать изменение размеров. В соответствии со специ­
фикацией HTML 4.0 указание размеров ячеек в процентах не допускается, однако
и Netscape, и Internet Explorer поддерживают подобный ф о р м а т значений. П р и
этом размеры ячейки вычисляются относительно ш и р и н ы и высоты таблицы, а не
относительно размеров окна броузера. Атрибуты WIDTH и HEIGHT не рекомендо­
ваны для применения. Вместо них желательно использовать соответствующие
средства листов стилей.

NOWRAP
Атрибут NOWRAP, также не рекомендованный для использования, запрещает пере­
нос слов внутри ячейки. Аналогичного эффекта можно добиться, разделяя слова
неразрывными пробелами ( & n b s p ; ) или включая в состав ячейки элемент PRE (в
некоторых броузерах это приведет также к отображению данных ш р и ф т о м фик­
сированной ш и р и н ы и появлению нежелательных символов перевода строки).
Используя атрибут NOWRAP, следует соблюдать осторожность, так как при этом
текст может оказаться за пределами окна броузера.

BGCOLOR и BACKGROUND
Атрибут BGCOLOR (спецификацией HTML 4.0 он не рекомендован для примене­
ния) задает цвет ф о н а для ячейки таблицы. Атрибут BACKGROUND, поддерживае­
мый Internet Explorer 3.0 и более новыми версиями, определяет файл, содержа­
щий изображение, которое отображается в качестве ф о н а ячейки. Если размеры
ячейки превышают размеры изображения, оно отображается многократно.

BORDERCOLOR, BORDERCOLORDARK, BORDERCOLORLIGHT


Данные атрибуты, поддерживаемые только Internet Explorer, задают цвет обрам­
ления ячейки. BORDERCOLOR определяет основной цвет, а BORDERCOLORDARK и
BORDERCOLORLIGHT— цвета для отображения теней при имитации трехмерного
2 . 4 . Таблицы 75

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


BORDER элемента TABLE не равно нулю.
Новые атрибуты ABBR, HEADERS, SCOPE и AXIS поддерживаются относительно
редко. ABBR, HEADERS и SCOPE предоставляют упрощенную информацию для невизу­
альных броузеров. Атрибут AXIS (в настоящее время он не поддерживается) связыва­
ет с ячейкой данные для выделения в запросе броузера.

Группировка содержимого таблицы


Элемент: <THEAD . . . > . . . </THEAD> (закрывающий дескриптор
молсет отсутствовать)
<TBODY . . . > . . . </TBODY> (закрывающий дескриптор
молсет отсутствовать)
<TFOOT . . . > . . . </TFOOT> (закрывающий дескриптор
молсет отсутствовать)
Атрибуты: ALIGN, VALIGN
Элементы THEAD, TBODY и TFOOT позволяют группировать фрагменты таблицы в
логические разделы. П р и использовании совместно с атрибутом RULES="GROUPS"
элемента TABLE эти фрагменты выделяются в наборы строк. В составе таблицы до­
пустим один раздел заголовков (header section), один раздел сносок (footer section) и
любое количество разделов тела таблицы (body section). В состав каждого раздела
может входить любое количество строк таблицы, сформированных посредством де­
скриптора TR. Все строки, входящие в группу, форматируются одновременно, это го­
раздо удобнее, чем задавать формат для каждой строки. Первоначально элементы
группировки создавались как расширение, специфическое для Internet Explorer. Впо­
следствии они были п р и н я т ы в качестве стандарта H T M L 4.0. Броузер Netscape 4.x не
поддерживает данные элементы.

Внимание!

Netscape 4.x не поддерживает элементы группировки THEAD TBODY


и TFOOT.

ALIGN и VALIGN
Атрибут ALIGN (допустимые значения: LEFT, RIGHT, CENTER, JUSTIFY и CHAR) за­
дает горизонтальное выравнивание для всех ячеек в группе. По умолчанию для
элемента THEAD принимается значение CENTER, а для элементов TBODY и TFOOT —
значение LEFT. Тип выравнивания CHAR был описан при рассмотрении элемента
TR. Атрибут VALIGN (допустимые значения: ТОР, MIDDLE, BOTTOM и BASELINE) за­
дает тип вертикального выравнивания. П о умолчанию принимается значение
MIDDLE. Т и п ы вертикального выравнивания были описаны при рассмотрении
элемента TR.
76 Глава 2. Элементы блокового уровня HTML 4.0

В листинге 2.14 приведен пример несложной таблицы, в которой выделены раздел


заголовков, раздел сносок и разделы тела таблицы. На рис. 2.10 показано, как эта таб­
лица отображается в окне броузера Internet Explorer 5.0, выполняющегося в среде
Windows 2000 Professional. Значение GROUPS атрибута RULES указывает на то, что раз­
делы таблицы должны отделяться друг от друга горизонтальными линиями.

Листинг 2.14. Формирование разделов в таблице

<TABLE B0RDER=1 CELLPADDING=4 RULES="GROUPS">


<CAPTION>Table Groups
<THEAD>
<TR><TH>Table Head<TH>Table Head
<TBODy>
<TR><TD>Group l<TD>Group 1
<TR><TD>Group l<TD>Group 1
<TBODy ALIGN="RIGHT">
<TR><TD>Group 2<TD>Group 2
<TR><TD>Group 2<TD>Group 2
<TFOOT ALIGN="CENTER">
<TR><TD C0LSPAN=2>Footer
</TABLE>

И Fie E<k Vtetv FavorRes Tools: Help 1^1


j >^-Beck - »*> " ^ J 3 i:2f ^Search »
J
Table Groups
1 Table Groups
I i Table Head Table Head !
1 Group 1 Group 1
1 j Group 1 Group 1
i Group 2 Group 2
Group 2 Group 2
Footer
Рис. 2.10. Внешний вид таблицы
с разделами, код которой
•^'Oone ' " ' ''"' Г|р'Intern^'"
содержится в листинге 2.14

Элементы: <COLGROUP . . . > . . . </COLGROUP> (закрывающий


дескриптор молсет отсутствовать)
<COL . . . > . . . </COL> (закрывающий дескриптор
может отсутствовать)
Атрибуты: ALIGN, VALIGN, SPAN, WIDTH
Элемент COLGROUP, который первоначально создавался как расширение Internet
Explorer, предназначен для группировки столбцов таблицы. Данный элемент помогает
2 . 4 . Таблицы 77

управлять форматированием различных столбцов. Без элемента COLGROUP для установ­


ки необходимого формата столбца пришлось бы непосредственно задавать формат со­
ответствующей ячейки в каждой строке. Наиболее часто COLGROUP используется для
управления горизонтальным выравниванием текста в столбцах. Если в составе откры­
вающего дескриптора TABLE задан атрибут RULES="GROUPS", то при отображении в
окне броузера группы, сформированные с помощью COLGROUP, отделяются друг от дру­
га вертикальными линиями. Для управления форматированием отдельных столбцов в
составе COLGROUP используются элементы COL. Элемент COL указывается непосредст­
венно после открывающего дескриптора COLGROUP. Заметьте также, что элемент
COLGROUP должен быть расположен перед элементом THEAD или TBODY.

ALIGNHVALIGN
Атрибут ALIGN (допустимые значения: LEFT, RIGHT, CENTER, JUSTIFY и CHAR) за­
дает горизонтальное выравнивание для всех ячеек, объединенных с помощью
COLGROUP или COL. По умолчанию принимается значение LEFT, за исключением
тех случаев, когда ячейки объединены с помощью THEAD. Ячейки, принадлежащие
группе, созданной с помощью THEAD, по умолчанию выравниваются по центру.
Однако следует заметить, что выравнивание, заданное явно, изменяет тип вырав­
нивания для раздела заголовка. С этим побочным эффектом легко справиться, за­
давая атрибут ALIGN для каждого из элементов ТН либо используя соответствую­
щие средства листов стилей. Атрибут VALIGN (допустимые значения: ТОР, MIDDLE,
BOTTOM и BASELINE) задает вертикальное выравнивание для всех ячеек в группе.
По умолчанию принимается значение MIDDLE. Значение BASELINE было описано
при рассмотрении атрибута ALIGN элемента TR. Следует заметить, что значения
JUSTIFY, CHAR и BASELINE поддерживаются не всеми броузерами.

SPAN и WIDTH
Атрибут SPAN указывает, какое количество последовательно расположенных
столбцов принадлежит элементу COLGROUP или COL. Атрибут WIDTH задает размер
группы COLGROUP или COL в процентах от ширины таблицы.
В листинге 2.15 показан простой пример группировки столбцов таблицы и управ­
ления выравниванием каждого столбца. Один из элементов COLGROUP, расположен­
ный перед элементом THEAD, связывается с первыми тремя столбцами, а второй эле­
мент COLGROUP — с оставшимся столбцом. Тип выравнивания каждого столбца в пер­
вой группе задается с помощью элемента COL. На рис. 2.11 показано, как таблица
отображается в окне броузера Internet Explorer 5.0, выполняющегося в среде Windows
2000 Professional. Поскольку в открывающем дескрипторе TABLE указано значение
GROUPS атрибута RULES, группы столбцов разделяются линией.

Листинг 2 . 1 5 . Управление выравниванием ячеек в столбцах и отображением


разделительных линий

<TABLE CELLPADDING=3 RULES="GROUPS">


<CAPTION>Stout Medal Award</CAPTION>
<COLGROUP>
<COL ALIGN="CENTER">
78 Глава 2. Элементы блокового уровня HTML 4.0

<COL ALIGN="LEFT">
<COL ALIGN="CENTER">
<COLGROUP ALIGN="RIGHT">
<THEAD>
<TR><TH>Year<TH>Cultivar<TH>Bloom Season<TH>Cost
<TBODY>
<TR><TD>1965<TD>Luxury Lace <TD>M <TD>11.75
<TR><TD>197 6<TD>Green Flutter<TD>M <TD> 7.50
<TR><TD>1984<TD>My Belle <TD>E <TD>12.00
<TR><TD>1985<TD>Stella De Oro<TD>E~L<TD> 5.00
<TR><TD>1989<TD>Brocaded Gown<TD>E <TD>14.50
<TFOOT>
<TR><TD C0LSPAN=4>E-early M - m i d s e a s o n L - l a t e
</TABLE>

L^fi^^'^?!'7''^^a;!'7l-li^ft^ -lDiiSj|
' FSe Edt View Favoritej Took НЫр

! ^^BacK ' «- - .'^1 J 3 :2} bse«ch ^ y Favorites

Table Colgi cups


1 stout Medal Award
1 Year Cultivar Bloom Season Cost
j 1965 Luxury Lace M 11.75 j
I 1976 Green Flutter M 7.50
1984 MyBeUe E 12.00
1985 SteUaDeOro E-L 5.00
1989 Brocaded Gown E 14.50 Рис. 2.11. Отображение таблицы, код
E-early M-midseason L-late которой приведен в листинге 2.15;
элементы COLGROUP И COL используются
для управления выравниванием и
^Oone 1Ф Interne
отображением разделительных линий

2.5. Формы
Элемент FORM позволяет организовать ввод данных в документ. Пользователь при
просмотре Web-страницы заполняет форму, и введенные данные передаются на сер­
вер для обработки. Как привило, в состав элемента FORM входят поля и окна редакти­
рования текста, флажки опций, переьслючатели опций, списки и кнопки. Подробно
элемент FORM будет рассматриваться в главе 18.

2.6. Прочие элементы блокового уровня


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

Элемент: <HR ...> (закрывающий дескриптор отсутствует)


Атрибуты: ALIGN, WIDTH, SIZE, NOSHADE, COLOR (нестандартный)
Горизонтальная линия, создаваемая посредством элемента HR, используется для
разделения различных частей документа. Длина линии может быть равной ширине
окна броузера или ее части. Н и ж е описаны атрибуты дескриптора <HR>. Все атрибуты
не рекомендованы для применения. Аналогичного э ф ф е к т а автор документа может
добиться, используя средства листов стилей.

ALIGN
Атрибут ALIGN задает горизонтальное выравнивание. Допустимыми значениями
являются LEFT, RIGHT и CENTER. П о умолчанию принимается значение CENTER.

WIDTH
Атрибут WIDTH задает длину линии в пикселях (<HR WI DTH=7 5>) либо в процентах
от ширины окна броузера (<HR WIDTH="50%">). П о умолчанию принимается зна­
чение 100%.

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

NOSHADE
Если задан атрибут NOSHADE, л и н и я отображается без затенения, создающего эф­
фект "гравировки".

COLOR
COLOR— нестандартный атрибут, поддерживаемый Internet Explorer. Д а н н ы й ат­
рибут изменяет цвет линии. Цвет задается в формате RGB (см. табл. 1.1).

Элемент: <DIV .-> ... </DIV>


Атрибут: ALIGN
Элемент DIV разбивает документ на разделы. Тип выравнивания каждого раздела
контролируется посредством атрибута ALIGN или с помощью листов стилей. Допус­
тимыми значениями атрибута ALIGN являются LEFT, RIGHT, CENTER и JUSTIFY.
В HTML 4.0 атрибут ALIGN не рекомендован для использования, поэтому авторы до­
кументов должны отдавать предпочтение каскадным листам стилей.

Элемент: <CENTER>... </CENTER>


Атрибуты: отсутствуют
Элемент CENTER не рекомендован для применения в HTML 4.0 и выполняет те же
действия, что и выражение <DIV ALIGN= "CENTER">, часто используется для обеспе­
чения совместимости с ранними версиями броузеров.
80 Глава 2. Элементы блокового уровня HTML 4.0

Элемент: <SCRIPT TYPE="..." ...> ... </8СЮРТ>


Атрибуты: TYPE (обязательный), LANGUAGE, SRC, DEFER, CHARSET
Элемент SCRIPT используется для включения программ, чаще всего написанных
на языке JavaScript. Подробно элемент SCRIPT и JavaScript-программы будут описаны
в главе 24.

Элемент: <NOSCRIPT>... </NOSCRIPT>


Атрибуты: отсутствуют
Содержимое элемента NOSCRIPT игнорируется броузерами, поддерживающими
JavaScript. С помощью этого элемента представляется альтернативный текст для бро­
узеров, которые не могут обработать JavaScript-программу. Дополнительные сведения
об использовании этого элемента вы найдете в главе 24.

Элемент: <MULTICOL CQLS=xx)c...>... </MULTICOL> (нестандартный)


Атрибуты: COLS (обязательный), GUTTER, WIDTH
Элемент MULT I COL представляет собой Netscape-расширение. Впервые этот эле­
мент был введен в Navigator 3.0 для отображения текста в несколько колонок, как в га­
зетах и журналах. Текст, отформатированный с помощью элемента MULTICOL, отли­
чается от обычной HTML-таблицы тем, что в очередной колонке автоматически рас­
полагается текст, являющийся продолжением предыдущей, а также тем, что ширина
всех колонок одинакова. В таблицах фрагменты текста жестко связаны с ячейками, а
ширина столбцов может быть различной. Броузеры Netscape и Internet Explorer по­
зволяют назначать различным ячейкам различные фоновые изображения. Элемент
MULTICOL не обеспечивает такой возможности.

COLS
Этот обязательный атрибут задает число колонок для отображения текста. Значе­
ние COLS должно быть больше или равно двум.

GUTTER
Этот необязательный атрибут определяет расстояние в пикселях между колонка­
ми. По умолчанию используется значение 10.

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

2.7. Резюме
Элементы блокового уровня позволяют создавать основные компоненты Web-
страницы. С их помощью можно создавать заголовки, различные типы абзацев, спи­
ски, таблицы, формы для ввода данных и другие конструкции. Для того чтобы изме­
нить формат части текста внутри блокового элемента, надо использовать элементы
текстового уровня.
2.6. Прочие элементы блокового уровня 81

Как отмечалось в данной главе, некоторые элементы и атрибуты, соответствую­


щие HTML 3.2, согласно новой спецификации HTML 4.0 не рекомендованы для при­
менения. Эти элементы продолжают поддерживаться Netscape и Internet Explorer,
однако если автор использует их в документе, он должен включить промежуточную
декларацию DOCTYPE. Это необходимо для проверки синтаксиса документа. Многие
новые элементы (например, THEAD, COLGROUP и MULTICOL) поддерживаются не все­
ми броузерами, поэтому, применяя их, надо соблюдать осторожность.
ЭЛЕМЕНТЫ
ТЕКСТОВОГО
УРОВНЯ HTML 4.0

В этой главе...

• Использование физических стилей.


• Использование логических стилей.
• Определение гипертекстовых ссылок.
• Включение изображений.
• Создание активизируемых областей изображений.
• Встраивание аплетов, аудио-,
видео- и ActiveX-компонентов.
• Управление переносом на новую строку.
J~y\:EJ^zJ

В данной главе речь пойдет об изменении стилей последовательностей символов,


о создании гипертекстовых ссылок, разрывах строк и встроенных объектах.
Элементы текстового уровня выполняют эти задачи, задавая представление
текста в составе элементов блокового уровня. Присутствие элемента текстового
уровня не приводит к созданию нового абзаца. В состав элемента текстового уровня
могут быть включены другие элементы текстового уровня, но не могут входить эле­
менты блокового уровня.
При рассмотрении элементов, представленных в данном разделе, мы не будем об­
суждать атрибуты, связанные с листами стилей (см. главу 5) и с обработкой событий
JavaScript (см. главу 24). Не будут рассматриваться также атрибуты DIR, LANG и TITLE,
описанные в главе 1.

3 . 1 . Физические стили отображения


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

Элемент: <В>... </В>


Атрибуты: отсутствуют
Элемент В сообщает броузеру о том, что текст, расположенный между открываю­
щим и закрывающим дескрипторами, должен отображаться полужирным шрифтом.
Используя листы стилей, можно изменить тип отображения текста (для получения
дополнительной информации обратитесь к главе 5).
84 Глава 3. Элементы текстового уровня HTML 4.0

Элемент: <1> ... </1>


Атрибуты: отсутствуют
Элемент I указывает броузеру на то, что текст, расположенный между открываю­
щим и закрывающим дескрипторами, должен отображаться курсивом.

Элемент: <ТТ>... </ТТ>


Атрибуты: отсутствуют
При использовании элемента ТТ текст отображается шрифтом фиксированной
ширины.

Элемент: <U>... </U>


Атрибуты: отсутствуют
Элемент U, который, согласно спецификации HTML 4.0, не рекомендован для
применения, задает отображение символов с подчеркиванием.

Элемент: <SUB>... </SUB>


Атрибуты: отсутствуют
Элемент SUB указывает на то, что символы, помещенные между открывающим и
закрывающим дескрипторами, должны отображаться как нижний индекс.

Элемент: <SUP> ..• </SUP>


Атрибуты: отсутствуют
Элемент SUP задает отображение символов как верхнего индекса.

Элемент: <BIG>... </BIG>


Атрибуты: отсутствуют
Элемент BIG указывает броузеру на то, что текст, заключенный между открываю­
щим и закрывающим дескрипторами, должен отображаться шрифтом большего раз­
мера по сравнению с текстом текущего абзаца. Шрифт увеличивается на одну услов­
ную единицу по шкале, насчитывающей семь возможных размеров. Реальный размер
шрифта выбирается броузером. Дополнительная информация приведена далее в этом
разделе при рассмотрении элемента FONT.

Элемент: <SMALL>... </SMALL>


Атрибуты: отсутствуют
Элемент SMALL сообщает броузеру о том, что текст, заключенный между откры­
вающим и закрывающим дескрипторами, должен отображаться шрифтом меньшего
размера по сравнению с текстом текущего абзаца. Шрифт уменьшается на одну услов­
ную единицу по шкале, насчитывающей семь возможных размеров. Реальный размер
шрифта выбирается броузером. Дополнительная информация приведена далее в этом
разделе при рассмотрении элемента FONT.
3 . 1 . Ф и з и ч е с к и е стили о т о б р а ж е н и я символов 85

Элемент: < D E L > . . . < / D E L >


Атрибуты: CITE, DATE TIME
С помощью элемента DEL помечаются фрагменты, удаленные из предыдущих вер­
сий документа. В Internet Explorer 5.0 удаленный текст отображается перечеркнутым.
Netscape не изменяет представление текста. Необязательный атрибут CITE задает
URI документа, который объясняет, почему информация была удалена. Атрибут
DATETIME указывает дату и время, соответствующие моменту, в который материал
был помечен для удаления. З н а ч е н и е DATETIME задается в ф о р м а т е YYYY-MM-
DDThhrmm: ssTZD. Формат DATETIME подробно описан в документе h t t p : / / w w w .
w3.org/TR/html40/types.html#type-datetime.

Элемент: <INS>... </INS>


Атрибуты: CITE, DATETIME
Элемент INS выполняет действия, обратные по отношению к DEL. INS помечает
материал, добавленный в предыдущую версию документа. Internet Explorer 5.0 ото­
бражает новый текст с подчеркиванием, а Netscape не изменяет представление тек­
ста. Назначение атрибутов CITE и DATETIME такое же, как и для элемента DEL.

Элемент: <Q>... </Q>


Атрибут: CITE
Элемент Q предназначен для представления кратких фрагментов цитируемого
текста. Атрибут CITE задает URI документа, из которого извлечена цитата. В настоя­
щее время ни Netscape, ни Internet Explorer не поддерживают этот элемент.

Элемент: <BDO DIR="..." ...> ... </BDO>


Атрибут: DIR, LANG
Для символов Unicode и ISO задается направление слева направо (LTR — left to
right) или справа налево (RTL — right to left). Элемент BDO изменяет направление сим­
волов, содержащихся между открывающим и закрывающим дескрипторами. Атрибут
01R указывает направление, которое может быть либо LTR, либо RTL. Атрибут LANG,
значение которого зависит от регистра символов, определяет язык, на котором пред­
ставлен текст. Более подробную и н ф о р м а ц и ю см. в главе 1.
В листинге 3.1 представлены п р и м е р ы использования различных стилей, а на
рис. 3.1 показано, как данная Web-страница выглядит на экране броузера.

Листинг 3 . 1 . Пример определения стилей для отображения символов

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Physical C h a r a c t e r Styles</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>Physical C h a r a c t e r Styles</Hl>
<B>Bold</B><BR>
<I>Italic</I><BR>
<TT>Teletype (Monospaced)</TT><BR>
86 Глава 3. Элементы текстового уровня HTML 4.0

<U>Underlined</U><BR>
Subscripts: f<SUB>0</SUB> + f<SUB>l</SUB><BR>
Superscripts: x<SUP>2</SUP> + y<SUP>2</SUP><BR>
<SMALL>Sinaller</SMALL><BR>
<BIG>Bigger</BIG><BR>
<STRIKE>Strike Through</STRIKE><BR>
<B><I>Bold Italic</I></B><BR>
<BIG><TT>Big Monospaced</TT></BIG><BR>
<SMALL><I>Small Italic</I></SiyLALL><BR>
<FONT COLOR="GRAY">Gray</FONT><BR>
<DEL>Delete</DEL><BR>
<INS>Insert</INS><BR>
</BODY>
</HTML>

^ Physicai tbm-m^eitmvkss^^fmm^m
Ne Edit View Favorites loots Hdp

4->Всн:к ^ ^ "'41^] i 2 ' '^Search ^£jFavorй»5 ** Links

3
Physical Character Styles
Bold
Italic
T e l e t y p e (Monospaced)
Underlined
Subscnpts: ^ + fj
Superscripts: x^ + y^
Smaller
Bigger
S t n k e Tlii-ough
Bold Italic
Big Monospaced
SmaW Italic

Insert Рис. 3.1. Примеры использования стилей,


d отображаемые в броузере Internet Explorer 5.0
jejOone lyiSi, My Computer в среде Windows 2000 Professional

Элемент: <FONT ...> ... </FONT>


Атрибут: SIZE, COLOR, FACE (нестандартный)
Д е с к р и п т о р FONT определяет размер или цвет текста. Атрибут SIZE позволяет ав­
тору управлять размером шрифта, а атрибут COLOR дает возможность задавать цвет
для отображения текста, содержащегося между открывающим и закрывающим деск­
рипторами. Как вы уже знаете, элементы текстового уровня не могут включать эле­
менты блокового уровня. Поэтому, если вам надо изменить представление несколь­
ких последовательно расположенных абзацев, списков или таблиц, следует задать де­
скрипторы FONT для каждого из этих элементов. Для того чтобы определить цвет
всего документа, можно воспользоваться атрибутом дескриптора BODY, а размер
ш р и ф т а задается посредством атрибута BASE FONT (он будет описан ниже). Согласно
спецификации H T M L 4.0, элемент FONT не рекомендован для применения. Альтерна­
тивой ему служат средства каскадных листов стилей; с их помощью можно определить
размер, цвет и начертание ш р и ф т а для абзацев и разделов документа. Подробную
информацию по этому вопросу вы найдете в главе 5.
3 . 1 . Ф и з и ч е с к и е стили о т о б р а ж е н и я символов 87

SIZE
Атрибут SIZE может принимать абсолютное значение в пределах от 1 (наимень­
ший размер) до 7 (наибольший размер), а также относительные значения
(SIZE=" + 1", S I Z E = " - 1 " , S I Z E = " + 2 " и т.д.), которые задают изменения текущего
размера шрифта. Реальный размер шрифта для отображения выбирается броузе­
ром. Задавая абсолютные значения, соблюдайте осторожность. Если пользователь
самостоятельно определит ш р и ф т для отображения документа, абсолютные зна­
чения атрибута SIZE могут испортить внешний вид Web-страницы.
Методика профессионалов

По возможности избегайте задавать абсолютные значения размера


шрифта. Используйте относительные значения.

COLOR
Как и при указании цвета в составе элемента BODY, в качестве значения атрибута
COLOR задается логическое имя (допустимые имена цветов перечислены в
табл. 1.1) либо значение в ф о р м а т е RGB. Определяя цвет текста, надо следить за
тем, чтобы он не сливался с фоном.

FACE
Netscape и Internet Explorer поддерживают атрибут FACE, в качестве значения ко­
торого задается список имен шрифтов, разделенных запятыми. Если на клиент­
ской машине есть первый из ш р и ф т о в , указанных в списке, он используется при
отображении документа. В противном случае система проверяет, имеется ли в на­
личии второй ш р и ф т из списка и т.д. Если все ш р и ф т ы , указанные в списке, отсут­
ствуют, применяется ш р и ф т по умолчанию, т.е. броузер ведет себя так, как будто
атрибут FACE не был задан.

Элемент: <BASEFONT SIZE=xxx> (закрывающий дескриптор отсутствует)


Атрибут: SIZE (обязательный)
Элемент BASE FONT задает размер шрифта, используемый по умолчанию для ото­
бражения текста, не принадлежащего заголовкам. В качестве значения задаются аб­
солютные величины в диапазоне от 1 (наименьший размер) до 7 (наибольший раз­
мер). По умолчанию используется значение 3. Как и для элементов FONT, BIG и
SMALL, преобразование семи условных величин в реальные размеры ш р и ф т а выпол­
няется броузером.
Элемент BASE FONT не влияет на цвет текста. Для установки цвета можно восполь­
зоваться атрибутами элемента BODY либо FONT. Аналогичный результат можно полу­
чить, применяя каскадные листы стилей. Согласно спецификации HTML 4, элемент
BASEFONT не рекомендован для использования. П р и м е н я я данный элемент, следует
соблюдать осторожность, поскольку многие пользователи устанавливают размер
шрифта для отображения документов при настройке броузера.
88 Глава 3 . Э л е м е н т ы текстового уровня HTML 4 . 0

3.2. Логические стили отображения


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

Элемент: <ЕМ>... </ЕМ>


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

Элемент: <STRONG>... </STRONG>


Атрибуты: отсутствуют
Элемент STRONG задает "сильное выделение" содержащегося в нем текста. О б ы ч н о
текст в составе данного элемента отображается полужирным шрифтом.

Элемент: <CODE>... </CODE>


Атрибуты: отсутствуют
Элемент CODE используется для представления фрагментов программного кода.
Текст, содержащийся в составе данного элемента, обычно отображается ш р и ф т о м
фиксированной ширины. Не следует забывать, что некоторые символы, например "<"
и "&:" и другие, используются для разметки, поэтому их надо заменять выражениями
" & l t ; ", "&ашр;" и т.д. Список управляющих символов представлен в табл. 2.1.

Внимание!

Прежде чем поместить фрагмент кода в состав элементов CODE, SANP


или KBD, надо проверить его на наличие символов "<", "&" и т.д.

Элемент: <SAMP>... </SAMP>


Атрибуты: отсутствуют
Элемент SAMP используется для представления результатов выполнения про­
грамм. Подобно CODE, содержимое данного элемента отображается ш р и ф т о м фикси­
рованной ширины.
3.2. Логические стили отображения символов 89

Элемент: <KBD> ... </KBD>


Атрибуты: отсутствуют
Элемент KBD представляет данные, которые пользователь должен ввести с клавиа­
туры. Подобно CODE, содержимое элемента KBD отображается шрифтом фиксиро­
ванной ширины.

Элемент: <DFN>... </DFN>


Атрибуты: отсутствуют
Элемент DFN используется для представления терминов. Internet Explorer отобра­
жает текст, находящийся в составе данного элемента, курсивом. Netscape применяет
для отображения текущий стиль и не изменяет внешний вид терминов по сравнению
с остальным текстом.

Элемент: <VAR>... </VAR>


Атрибуты: отсутствуют
Элемент VAR представляет переменные или параметры функции либо процедуры.
Обычно текст, к о т о р ы й содержится между открывающим и закрывающим дескрип­
торами <VAR>, отображается курсивом.

Элемент: <С1ТЕ> ... </С1ТЕ>


Атрибуты: отсутствуют
Элемент CITE указывает на то, что содержащийся в нем текст представляет собой
цитату либо ссылку. Как правило, этот текст отображается курсивом.

Элемент: <ACRONYM>... </ACRONYM>


Атрибут: TITLE
Элемент ACRONYM часто используется совместно с атрибутом TITLE и предназна­
чен для расшифровки сокращенного названия в процессе работы. Например:
<ACRONYM TITLE="Java D e v e l o p m e n t Kit">JDK</ACRONYM>
Если курсор мыши располагается над подобным элементом, Internet Explorer 5.x
выводит окно, в котором отображается значение атрибута TITLE. Netscape не выво­
дит значение TITLE.
В листинге 3.2 представлены примеры логических стилей. Результаты отображе­
ния приведенных выражений в окне броузера показаны на рис. 3.2.

Листинг 3.2. Примеры логических стилей

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Logical C h a r a c t e r S t y l e s < / T I T L E >
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>Logical C h a r a c t e r S t y l e s < / H l >
90 Глава 3. Элементы текстового уровня HTML 4.0

<EM>Emphasized</EM><BR>
<STRONG>Strongly Emphasized</STRONG><BR>
<CODE>Code</CODE><BR>
<SAMP>Sample Output</SAMP><BR>
<KBD>Keyboard Text</KBD><BR>
<DFN>Definition</DFN><BR>
<VAR>Variable</VAR><BR>
<CITE>Citation</CITE><BR>
<EM><CODE>Emphasized Code</CODEX/EM><BR>
<FONT COLOR="GRAY"><CITE>Gray Citation</CITE></FONT><BR>
<ACRONYM TITLE="Java Development Kit">JDK Acronym</ACRONYM>
</BODY>
</HTML>

iMfWTH^ni-i!^.,.!!, I Hl.l,..i i U L I „ . l J — p^jgfxf


ЮН
\ ^- J J ^ a -±j J -Ji- ^^3s_ -_' ~
^
Logical Character Styles
[| Emphasized
1 Strongly Emphasized
1 Code
:| Semple Output
1 Keyboard Text
I Dejinilion
1 Variable
1 Citation
1 Emphasized Code
Рис. 3.2. Отображение логических
1 JDK Acronym
1 ^ [Java Developmenl K.it]
стилей в окне Internet Explorer 5.0,
^ выполняющегося в среде Windows 2000
.ij^Done : i My CoflfipUer '¥Щ Professional

Элемент: <ABBR ...> ... </ABBR>


Атрибут: TITLE
Элемент ABBR, пример использования которого приведен ниже, отображает аб­
бревиатуру.
<ABBR TITLE="cubic inches">cu. in.</ABBR>
Ни Netscape, ни Internet Explorer не поддерживают этот элемент.

Элемент: <SPAN ...> ... </SPAN>


Атрибуты: CLASS, ID, STYLE
SPAN— это элемент текстового уровня, предназначенный для поддержки каскад­
ных листов стилей. Элемент SPAN будет рассмотрен в главе 5.

3.3. Построение гипертекстовых ссылок


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

возможность переходить по ссылкам и отображать информацию, наиболее интересую­


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

Элемент: <А ...>... </А>


Атрибуты: HREF, NAME, TARGET, REL, REV, TITLE, ONFOCUS, ONBLUR, COORDS,
SHAPE,TYPE, HREFLANG, CHARSET, ACCESSKEY, TABINDEX
Элемент A представляет собой контейнер, причем закрывающий дескриптор обя­
зательно должен быть задан. Если в составе элемента А указан атрибут HREF, содер­
жимое элемента становится чувствительным к действиям мыши. П р и активизации
данного элемента броузер отображает ресурс, на который указывает значение атри-
буга HREF. Текст, помещенный между открывающим и закрывающим дескрипторами
<А>, обычно отображается с подчеркиванием и выводится синим цветом либо цве­
том, заданным посредством атрибута LINK элемента BODY. Цвет для отображения ги­
пертекстовых ссылок может быть также определен с помощью листов стилей. Ссыл­
ки, указывающие на документы, которые были просмотрены пользователем в теку­
щем сеансе, обычно выводятся с подчеркиванием и отображаются цветом, заданным
с помощью атрибута VLINK. В зависимости от механизма отслеживания предыстории
работы, ссылки на документы, просмотренные в предыдущих сеансах, также могут
отображаться подобным образом.

HREF
Атрибут HREF задает адрес ресурса, который броузер должен загрузить в том слу­
чае, если пользователь активизирует ссылку (щелкнет кнопкой мыши в указанной
области). Значением HREF может быть абсолютный URL, относительный URL,
символ "#" за которым следует имя позиции в документе (см атрибут NAME), либо
URL, после которого указан символ "#", сопровождаемый именем позиции. Если
указан конкретный раздел документа, при активизации ссылки броузер копирует
этот документ и отображает его, начиная с заданной позиции. В противном случае
броузер отображает начало требуемого документа.
Как будет показано в главе 19, если в URL, определяющем каталог, не указана за­
вершающая косая черта, броузер устанавливает два соединения с HTTP-сервером.
Во время первого соединения броузер получает корректный URL, а во время вто­
рого соединения он отправляет запрос, к о т о р ы й содержит URL, скорректирован­
ный в соответствии с полем L o c a t i o n в ответе HTTP-сервера. Если производи­
тельность линий связи невелика, лишний запрос приводит к ненужной трате вре­
мени. Поэтому вместо http://some.host.com/some/directory всегда
задавайте h t t p : / / s o m e . h o s t , c o m / s o m e / d i r e c t o r y / .

Совет

Если URL указывает на каталог, убедитесь, что в его составе указана


завершающая косая черта.
92 Глава 3 . Э л е м е н т ы текстового уровня HTML 4 . 0

В листинге 3.3 показаны ч е т ы р е типа гипертекстовых ссылок, содержащих абсо­


лютный URL, относительный URL, указывающих на позицию внутри текущего до­
кумента, а также на позицию в документе с заданным URL. Помимо ссылок h t t p : ,
большинство броузеров также поддерживают ссылки m a i l t o : (для адресов элек­
т р о н н о й почты), f i l e : (для локальной файловой системы на клиентской маши­
не) и f t p : (для FTP-узлов).

Листинг 3.3. Гипертекстовые ссылки

The official HTML specifications are available from


<A HREF="http://www.w3.org/MarkUp/"> the World Wide Web
Consortium (W3C)</A>, with some examples given in
<A HREF= "HTML-Examples .html">my example page</A>.
The Java programming language is discussed in
<A HREF="#Section-3">Section 3</A>. For a discussion of COBOL, see
<A HREF="johndoe.html#COBOL">my husband's home page</A>.

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


значительно удобнее, если текст, о ф о р м л е н н ы й как ссылка, отражает ее назначе­
ние. Подобный документ выигрывает по сравнению с тем, в котором описание
размещено до или после ссылки, а в состав элемента включается формальное вы­
ражение типа "click here". Н и ж е приведен пример гипертекстовой ссылки.
Recent Dilbert strips are available on-line at
<A HREF="http://www.unitedmedia.com/comics/dilbert/">
The Dilbert Zone</A>.
Такое оформление ссылки существенно выигрывает по сравнению с приведенным
ниже примером.
<А HREF="http://www.unitedmedia.com/comics/dilbert/">
Click here</A> to see recent Dilbert strips that are
available on-line at The Dilbert Zone.

М е т о д и к а профессионалов

Избегайте ссылок типа "click here". Старайтесь, чтобы текст,


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

NAME
Атрибут NAME присваивает имя позиции в документе. В результате на эту позицию
могут указывать ссылки, в которых значение атрибута HREF содержит символ "#".
Например:
<А NAME="COBOL">COBOL: А Programming Language f o r t h e Future</A>
Имя, задаваемое с помощью атрибута NAME, зависит от регистра символов.

TARGET
Атрибут TARGET указывает на то, что документ должен быть открыт в конкретном
кадре либо в новом окне. Атрибут TARGET будет подробно рассмотрен в главе 4.
3.3. Построение гипертекстовых ссылок 93

TITLE
Атрибут TITLE присваивает заголовок документу, в котором заголовок отсутствует
(например, каталогу FTP). В некоторых броузерах с помощью TITLE предлагается
тема электронного письма (поле s u b j e c t : ) . Значение TITLE может быть исполь­
зовано индексирующими программами для построения меню ссылок.

RELHREV
REL и REV применяются гораздо реже, чем другие атрибуты. С их помощью можно
определить отношение текущего документа к связанному (REL) и связанного доку­
мента — к текущему (REV).

ONFOCUS и ONBLUR
Эти атрибуты используются броузерами, поддерживающими JavaiScript. С их по­
мощью определяется JavaScript-код, обрабатывающий события, связанные с полу­
чением и потерей фокуса. Получение и потеря фокуса может происходить в ре­
зультате действий с клавиатурой или мышью. Дополнительную и н ф о р м а ц и ю вы
найдете в главе 24.

COORDS и SHAPE
Если гипертекстовая ссылка представляет карту изображений, связанную с CGI-
программой, атрибут COORDS задает координаты (х,у) области изображения. Ат­
рибут SHAPE указывает, как должны интерпретироваться пары (х,у). Д а н н ы е атри­
буты будут подробнее рассмотрены в разделе 3.5.

TYPE, CHARSET и H R E F L A N G
Атрибут TYPE определяет МШЕ-тип, связанный с ресурсом, на который указывает
ссылка, например TYPE=" t e x t / h t m l " . Атрибут CHARSET задает кодировку свя­
занного документа, например CHARSET="ISO-8859-6". Атрибут HREFLANG опре­
деляет язык ресурса, например, выражение HREFLANG="pt" означает, что доку­
мент, на который указывает ссылка, представлен на португальском языке.

ACCESSKEY и T A B I N D E X
Атрибут ACCESSKEY задает единичный символ (соответствующий кодировке доку­
мента), который используется в сочетании с клавишей <Alt> (в системе MS-
Windows) или <Cmd> (в системе Apple). П р и нажатии комбинации клавиш фокус
перемещается к соответствующему элементу. Атрибут TABINDEX указывает целое
число в диапазоне 0-32767, которое задает порядок элементов в документе. Если
пользователь перемещается по документу с помощью клавиатуры, TABINDEX опре­
деляет порядок получения фокуса элементами документа. По умолчанию все эле­
менты, не поддерживающие атрибут TABINDEX, получают значение 0. Элементы с
одинаковыми значениями получают фокус в том порядке, в котором они встреча­
ются в документе. Internet Explorer 5.0 поддерживает оба указанных атрибута.
Netscape 4.x не поддерживает ни ACCESSKEY, ни TABINDEX.
94 Глава 3. Элементы текстового уровня HTML 4.0

3.4. Встроенные изображения


Элемент IMG позволяет включать изображения в состав документа. Большинство
броузеров может обрабатывать форматы GIF (Graphic Interchange Format), JPEG (Joint
Photographic Expert Group) и PNG (Portable Network Graphic), кроме того, ряд броузе­
ров непосредственно либо с помощью дополнительных модулей поддерживает форма­
ты xbm, x m p и bmp. Для таких изображений, как чертежи, построенные с помощью
графических пакетов, GIF-представление более компактно по сравнению с представле­
нием в формате JPEG. Для оцифрованных фотоснимков и других изображений, харак­
теризующихся частыми изменениями цвета, размер JPEG-файлов, как правило, получа­
ется меньше по сравнению с GIF-файлами. Поскольку время копирования изображения
определяет время копирования всей страницы, желательно представлять изображение
в том формате, который обеспечивает минимальный размер файла.

Анимационные аГ-файлы
Многие броузеры поддерживают стандарт GIF89A, к о т о р ы й позволяет включать в
состав одного графического файла несколько кадров. Кадры сменяют друг друга через
определенный интервал времени, что создает э ф ф е к т простой анимации. Если бро­
узер поддерживает только формат GIF87, он может корректно отобразить первый
кадр в составе изображения GIF89A. GIF89A является альтернативой анимации на ба­
зе Java, при этом структура Web-страницы существенно упрощается. Многие про­
граммные пакеты позволяют создавать файлы GIF89A, объединяя несколько единич­
ных изображений, представленных в различных форматах, либо конвертируя анима­
ционные файлы AVI или QuickTime.

Элемент IMG
Элемент: <IMG SRC="..." ALT="..," ...> (закрывающий дескриптор
отсутствует)
Атрибуты: SRC (обязательный), ALT (обязательный), ALIGN, WIDTH, HEIGHT, HSPACE,
VSPACE, BORDER, USEMAP, ISMAP, NAME, LONGDESC, ONLOAD (нестандартный), ONERROR
(нестандартный), ONABORT (нестандартный)
Элемент IMG включает изображение в текущую позицию в документе. Заметьте,
что IMG представляет собой элемент текстового уровня и не приводит к созданию но­
вого абзаца. IMG не является контейнером, поэтому закрывающий дескриптор не ука­
зывается.

SRC
SRC — обязательный атрибут. О н определяет расположение файла, содержащего
изображение, которое должно быть включено в документ. Значением SRC может
быть как абсолютный, так и относительный URL. П р и м е р ы элементов IMG, со­
держащих атрибуты SRC, приведены ниже.
<IMG SRC="http://www.some-isp.сош/~jane/portrait.jpg"
ALT="Jane Doe">
<IMG SRC="images/spot.gif" WIDTH=150 HEIGHT=120
ALT="My dog Spot">
3 . 4 . Встроенные и з о б р а ж е н и я 95

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

WIDTH и H E I G H T
Эти два атрибута определяют раЗхМеры области, которую должно занимать изобра­
жение в документе. Размеры задаются в пикселях. Если в составе элемента IMG указа­
ны атрибуты WIDTH и HEIGHT, броузер может сначала отобразить текст, а затем за­
грузить и вывести изображение, не изменяя структуру страницы. Подобный подход
к отображению страницы улучшает ее восприятие пользователем. Если, работая с
Netscape, вы непосредственно загрузили изображение, то, выбрав пункт P a g e Info
меню View, вы можете определить размеры этого изображения. Размеры также ото­
бражаются в заголовке окна Netscape. В Internet Explorer 5.0, чтобы определить раз­
меры изображения, достаточно расположить поверх него курсор мыши, щелкнуть
правой кнопкой и выбрать в контекстном меню пункт Properties. Если значения ат­
рибутов WIDTH и HEIGHT не совпадают с реальными размерами изображения, изо­
бражение растянется или сожмется, чтобы соответствовать размерам, заданным в
составе дескриптора IMG. Если при загрузке размеры изображения изменились, то
при выборе пункта Properties из контекстного меню Internet Explorer сообщает не
исходные размеры изображения, а его размеры в окне броузера.

Методика профессионалов

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


атрибуты ALT, WIDTH и HEIGHT.

ALIGN
Атрибут ALIGN, который, согласно спецификации HTML 4.0, не рекомендован для
применения, определяет расположение изображения относительно текстовой
строки, в которой присутствует дескриптор <IMG>. Допустимыми значениями
данного атрибута являются LEFT, RIGHT, TOP, BOTTOM и MIDDLE. П о умолчанию
принимается значение BOTTOM. З н а ч е н и я LEFT и RIGHT задают обтекание изо­
бражения текстом и обычно указываются тогда, когда изображение используется в
качестве иллюстрации. Если вы указали значение LEFT или RIGHT и не хотите,
чтобы рядом с изображением выводился текст, надо использовать элемент <BR
CLEAR="ALL">. Подробно элемент BR будет рассматриваться далее в этой главе.
Значение MIDDLE применяется при работе с изображениями малого размера, вы­
полняющего функции маркера. В листинге 3.4 приведены различные примеры
выравнивания. Результаты показаны на рис. 3.3.
96 Глава 3. Элементы текстового уровня HTML 4.0

^b^3k:i^t^m4A^kMl^tM^^
Image Alignment
I Alignment Result
^ ^ ^ This positions the image at the left side, with text flowing around it
^w\ 5, ^ <5п the nght
LEFT .VJIOT^"

r^ : ^_____
This positions the image at the nght side, with text flowing around ^ ^ ^
it on the left '^'wi^N^'
RIGHT ,4Jp}^'

^ ^ ^ Here, the image runs into the paragraph and the line containing the

image is aligned with the image top.

Here, the image runs into the paragraph and the line containing the
iimage is aligned with the image bottom.

j9>,*^ Here, the image runs into the paragraph and Ле line containing the

image is aligned with the image center.

^Щ^х ' !C^Jaёtmж* DowT'^


"^Шт^^^ШШ^!^.
Рис. 3.3. Пять типов выравнивания изображений

Листинг3.4. Image~Alignment.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Iinage Alignment</TITLE>
</HEAD>
<BODY>
<H1 ALIGN="CENTER">Image Alignment</Hl>

<TABLE B0RDER=1>
<TR><TH>Alignment
<TH>Result
<TR><TH><CODE>LEFT</CODE>
<TD><IMG SRC="rude-pc.gif" ALIGN="LEFT"
ALT="Rude PC" WIDTH=54 HEIGHT=77>
This positions the image at the left side,
with text flowing around it on the right.
<TR><TH><CODE>RIGHT</CODE>
<TD><IMG SRC="rude-pc.gif" ALIGN="RIGHT"
ALT="Rude PC" WIDTH=54 HEIGHT=77>
This positions the image at the right side,
with text flowing around it on the left.
<TR><TH><CODE>TOP</CODE>
3.4. Встроенные изображения 97

<TD><IMG SRC="rude-pc.gif" ALIGN="TOP"


ALT="Rude PC" WIDTH=54 HEIGHT=77>
Here, the image runs into the paragraph
and the line containing the image is
aligned with the image top.
<TR><TH><CODE>BOTTOM</CODE>
<TD><IMG SRC="rude-pc.gif" ALIGN="BOTTOM"
ALT="Rude PC" WIDTH=54 HEIGHT=77>
Here, the image runs into the paragraph
and the line containing the image is aligned
with the image bottom.
<TR><TH><CODE>MIDDLE</CODE>
<TD><IMG SRC="rude-pc.gif" ALIGN="MIDDLE"
ALT="Rude PC" WIDTH=54 HEIGHT=77>
Here, the image runs into the paragraph
and the line containing the image is aligned
with the image center.
</TABLE>

</BODY>
</HTML>

HSPACE и VSPACE
Эти атрибуты задают размеры пустого пространства (в пикселях) слева и справа от
изображения (HSPACE), а также над и под изображением (VSPACE). Часто по умол­
чанию принимается значение 2. HTML 4.0 явно не определяет средства, обеспе­
чивающие перекрытие изображений и расположение текста над изображением,
но этот э ф ф е к т можно реализовать, задавая отрицательные границы в каскадных
листах стилей. Поскольку и HSPACE, и VSPACE не рекомендованы для применения,
мы рекомендуем использовать для контроля границ изображения листы стилей.
Подробные сведения о работе с листами стилей вы найдете в главе 5.

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

USEMAP
Атрибут USEMAP задает имя компонента MAP как " # n a m e " или "URL#name". Под­
робное описание и п р и м е р ы использования данного атрибута приведены далее в
этой главе.

ISMAP
Атрибут ISMAP задает использование изображения как карты, связанной с CGI-
программой. Данный атрибут допустим только в том случае, когда изображение
является частью гипертекстовой ссылки. Дополнительную и н ф о р м а ц и ю по этому
вопросу вы найдете в главе 18.
98 Глава 3. Элементы текстового уровня HTML 4.0

NAME
Атрибут NAME присваивает изображению имя. Если броузер поддерживает
JavaScript, программы на JavaScript в составе документа могут обращаться по этому
имени к изображению

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

ONLOAD, ONERROR и ONABORT


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

3.5. Карты изображений на стороне клиента


Элемент: <МАР NAME='..."> ... </МАР>
Атрибуты: NAME (обязательный)
Элемент MAP дает возможность автору документа создавать карты изображений па
стороне клиента. С помощью этого элемента можно связывать URL с разными облас­
тями изображения. Карты изображений используются для навигации, подобно обыч­
ным картам. Карты изображений полностью обрабатываются броузером, в этом они
отличаются от карт изображений на стороне сервера, для создания которых используется
ISMAP или специальный тип элемента INPUT (оба элемента описаны в главе 18). Для
того чтобы определить необходимую операцию для карты изображений, созданной с
помощью ISMAP или INPUT, необходимо установить соединение с сервером. Атрибут
NAME является обязательным и используется как целевое имя для атрибута USEMAP
элемента IMG. Каждая из областей, допускающих активизацию с помощью мыши,
описывается посредством элемента AREA, к о т о р ы й располагается между открываю­
щим и закрывающим дескрипторами MAP.
В листинге 3.5 приведен пример изображения, разделенного на четыре квадранта.
С каждым из квадрантов связан HTML-документ. Изображения в окне броузера до и
после щелчка на нижнем левом квадранте показаны соответственно на рис. 3.4 и 3.5.
Для отображения данного примера использовался броузер Internet Explorer 5.0, вы­
полняющийся в среде Мае OS 9.

Листинг 3.5. Карта изображений

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Kansas Topography</TITLE>
</HEAD>
<BODY>
3.5. Карты изображений на стороне клиента 99

<Н1 ALIGN="CENTER">Kansas Topography</Hl>


Click on a region of Kansas to get information on
the terrain in that area.
<P>
<IMG SRC="kansas.gif" ALT="Kansas" WIDTH=385 HEIGHT=170
USEMAP="#Kansas" BORDER=0>
<MAP NAME="Kansas">
<AREA HREF="nw.html"
SHAPE="RECT"
COORDS="0,0, 192, 85"
ALT="North West">
<AREA H R E F = " n e . h t m l "
SHAPE="RECT"
COORDS="193, 0 , 3 8 5 , 8 5 "
ALT="North E a s t " >
<AREA H R E F = " s w . h t m l "
SHAPE="RECT"
COORDS="0,86,192,170"
ALT="South West">
<AREA H R E F = " s e . h t m l "
SHAPE="RECT"
COORDS="193,86, 3 8 5 , 1 7 0 "
ALT="South E a s t " >
</MAP>
</BODY>
</HTML>

) Kdnsas Topoerephy i :гф Southwest Й


mm mm
t^ Ш Ф й »e
Kansas Topography Flat
Gick on a region of Kansas to get information on the teiiain The southwest legion is flat. Vv'hat did you think this was.
m that area Colorado?

(j^ link: http://<w«v«feprft9r*mm|r>q/Saure»/Chai>t»r3;/sw.Mml } ht«ffi«t ?on«

Рис. 3.4. Карты изображений на стороне Рис. 3.5. Результат активизации нижнего ле­
клиента позволяют связывать Web-страницы с вого квадранта изображения на рис. 3.4
различными фрагментами изображения

Элемент: <AREAALT="..." ...> (закрывающий дескриптор отсутствует)


Атрибуты: HREF, COORDS, SHAPE, ALT ( о б я з а т е л ь н ы й ) , NOHREF, TARGET, ONFOCUS,
ONBLUR, ACCESSKEY, TABINDEX
Э л е м е н т AREA м о ж е т п р и с у т с т в о в а т ь т о л ь к о в с о с т а в е э л е м е н т а MAP. О н о п и с ы в а е т
область к а р т ы и з о б р а ж е н и я , к о т о р а я д о п у с к а е т а к т и в и з а ц и ю с п о м о щ ь ю м ы ш и . К а ж -
100 Глава 3. Элементы текстового уровня HTML 4.0

дой области соответствует один элемент AREA, определяющий расположение активи­


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

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

COORDS
Атрибут COORDS содержит список координат области, разделенных запятыми.
Число координат и порядок их обработки зависят от значения атрибута SHAPE.
Координаты должны задаваться целыми числами и интерпретироваться как рас­
стояние в пикселях от верхнего левого угла изображения.

SHAPE
К допустимым значениям атрибута SHAPE относятся RECT (принимается по умол­
чанию), CIRCLE, POLY и DEFAULT. Если значение не равно DEFAULT, атрибут
SHAPE должен использоваться совместно с атрибутом COORDS. Значению RECT со­
ответствуют координаты, заданные в формате "левая граница, верхняя граница,
правая граница, нижняя граница". Н а п р и м е р , следующее выражение связывает
документ r e g i o n 3 . h t m l с областью размером 200x200 пикселей, левый верхний
угол которой расположен в точке (20,40):
<AREA H R E F = " r e g i o n 3 . h t m l "
SHAPE="RECT"
COORDS="20,40,220,240">
Если указано значение CIRCLE, координаты задаются посредством атрибута
COORDS в виде "х,у,радиус". З н а ч е н и е POLY требует координат в виде "х1, у1,х2,
y2,...,xN, yN", определяющих вершины многоугольника. Значение DEFAULT не
предполагает использования атрибута COORDS и означает все изображение цели­
ком. URL, принадлежащий перекрывающимся областям, определяется посредст­
вом первого элемента AREA. Область по умолчанию всегда определяется послед­
ней, независимо от того, указан ли в элементе AREA атрибут SHAPE со значением
DEFAULT.

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

NOHREF
Атрибут NOHREF определяет область, с которой не связан конкретный URL. Этот
атрибут необходим только в том случае, когда вы хотите исключить из числа акти-
3 . 6 . Включение объектов в д о к у м е н т 101

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


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

Поскольку URL выбирается в соответствии с областью, описанной с


помощью первого элемента AREA, элемент с дескриптором NOHREF
должен быть расположен первым в составе элемента MAP. Элемент,
определяющий URL по умолчанию, должен быть расположен по­
следним. #

TARGET
Атрибут TARGET определяет ф р е й м , в котором должны отображаться результаты.
Карты изображений часто используются в документах с фреймами. Фреймы будут
подробно описаны в главе 4.

O N F O C U S , O N B L U R , ACCESSKEY и T A B I N D E X
Эти атрибуты используются так же, как и в элементе IMG, описанном ранее.

3.6. Включение объектов в документ


Поскольку Web-клиенты, поддерживающие только текст, встречаются крайне ред­
ко, почти все броузеры могут обрабатывать встроенные файлы изображений. Однако
изображения — не единственный тип объекта, который может быть включен в состав
Web-страницы. Большинство броузеров также поддерживает Java-аплеты. П р о ч и е ау­
дио-, видео-, VRML- и ActiveX-объекты обычно обрабатываются дополнительными
модуляхМИ, встраиваемыми в состав броузера.

Встроенные аплеты
Э л е м е н т : <APPLET CODE="..." W I D T H = x x x H E I G H T = x x x ...> ... < / A P P L E T >
Атрибуты: CODE, WIDTH (обязательный), HEIGHT (обязательный), CODEBASE, ALT, ALIGN,
HSPACE, VSPACE, NAME, OBJECT, ARCHIVE (нестандартный), MAYSCRIPT (нестандартный)
Дескриптор APPLET позволяет включать в состав Web-страниц аплеты. П о д р о б н о
этот вопрос будет рассмотрен в главе 9.

CODE
Атрибут CODE определяет имя Java-файла класса, который должен быть загружен
броузером. Атрибу'т CODE является обязательным за исключением тех случаев, ко­
гда указан атриб)т OBJECT. CODE не задает абсолютный URL, а лии1Ь указывает имя
файла. Если атрибут CODEBASE отсутствует, считается, что файл находится в том
же каталоге, что и Web-страница.
102 Глава 3 . Э л е м е н т ы текстового уровня H T M L 4 . 0

На з а м е т к у

Атрибут CODE нельзя использовать для определения абсолютного


URL. Если расположение аплета отличается от расположения теку­
щего документа, следует применять атрибут CODEBASE.

WIDTH и HEIGHT
Эти атрибуты определяют размеры области в окне броузера, которую должен за­
нимать аплет. Размеры задаются в пикселях либо в процентах от ширины окна
броузера. Следует заметить, что программа a p p l e t v i e w e r , разработанная Sun
(упрощенный броузер, который игнорирует все HTML-дескрипторы за исключе­
нием APPLET), не поддерживает размеры, заданные в процентах, поскольку окно,
относительно которого должны вычисляться размеры, не определено. Атрибуты
WIDTH и HEIGHT обязательны для всех аплетов.

Внимание!

Программа appletviewer, разработанная Sun, не поддерживает


значения атрибутов WIDTH и HEIGHT, заданные в процентах.

CODEBASE
Атрибут CODEBASE определяет URL базового каталога. Из этого каталога загружа­
ется файл, указанный посредством атрибута CODE. Использование данного атрибу­
та описано в главе 9.

ALT
Броузеры, поддерживающие Java, игнорируют все элементы разметки, располо­
женные между дескрипторами <APPLET . . . > и </APPLET>, поэтом)' в области, в
которой должен быть расположен аплет, отображается альтернативный текст.
Атрибут ALT предназначен для тех броузеров, которые поддерживают Java, но в
которых возможность обработки аплетов отключена. Атрибут ALT поддерживает­
ся сравнительно редко, поэтому мы не рекомендуем использовать его.

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

HSPACE
Атрибут HSPACE задает размеры пустого пространства в пикселях слева и справа
от аплета.

VSPACE
Атрибут VSPACE задает размеры пустого пространства в пикселях снизу и сверху
от аплета.
3 . 6 . Включение объектов в д о к у м е н т 103

NAME
Атрибут NAME присваивает аплету имя, которое используется для организации
взаимодействия между аплетами, а также при обращении к аплету JavaScript-
программы.

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

ARCHIVE
Атрибут ARCHIVE определяет архив классов, которые должны быть заранее загру­
жены. Архив задается в ф о р м а т е Java Archive (. j a r ) , однако Netscape поддержива­
ет также архивы uncompressed Zip (. z i p ) .

MAYSCRIPT
Netscape и Internet Explorer с помощью этого атрибута определяют, можно ли
управлять аплетом из JavaScript-программы. Дополнительную и н ф о р м а ц и ю по
этому вопросу вы найдете в главе 24.
Согласно спецификации HTML 4.0 элемент APPLET и все его атрибуты не реко­
мендованы для использования. В качестве альтернативы предлагается элемент
OBJECT. Тем не менее многие авторы предпочитают элемент APPLET как более про­
стой. Дополнительные сведения об элементах APPLET и OBJECT будут приведены в
главе 9.

Элемент: <PARAM NAME="..." VALUE="..."> (закрывающий


дескриптор отсутствует)
Атрибуты: NAME (обязательный), VALUE (обязательный)
Элемент РАКАМ предназначен для передачи значений аплету, который затем читает
их с помощью метода g e t Ра ram. Подробно использование элемента PARAM описано в
главе 9. Параметры с именами WIDTH и HEIGHT изменяют значения атрибутов WIDTH и
HEIGHT, поэтому следует избегать использования элементов <PARAM NAME= "WIDTH"
. . . > и <PARAM NAME="HEIGHT" . . . >. Элементы PARAM могут быть использованы со­
вместно с элементом OBJECT, который будет описан далее в этом разделе.

Внимание!

Старайтесь не использовать имена WIDTH и HEIGHT в составе объекта


PARAM.
104 Глава 3. Элементы текстового уровня HTML 4.0

Встроенные аудио- и видеофрагменты, а также


объекты в других форматах

Элемент: <EMBED SRC="..." ...> ... </EMBED>


Атрибуты: SRC, WIDTH, HEIGHT, a также атрибуты, специфические для дополнитель­
ных модулей
Элемент EMBED не предусмотрен в спецификации HTML 4.0, но может быть ис­
пользован для создания встроенных объектов, т и п ы которых поддерживаются до­
полнительными модулями конкретного броузера. Если броузер содержит дополни­
тельный модуль, он игнорирует текст в составе элемента. Помимо SRC, WIDTH и
HEIGHT в открывающем дескрипторе могут быть указаны атрибуты, специфические
для конкретных дополнительных модулей. Как Netscape, так и Internet Explorer вклю­
чает стандартные модули LiveVideo, предназначенные для воспроизведения видео­
клипов в ф о р м а т е AVI. Эти мод)^ли поддерживают специальный атрибут AUTOSTART,
который может принимать значение TRUE или FALSE. Если указано значение TRUE,
видеофрагмент воспроизводится автоматически при загрузке Web-страницы. Значе­
ние FALSE, принимаемое по умолчанию, указывает на то, что воспроизведение клипа
начинается по команде пользователя. Приведенное ниже выражение включает в со­
став документа видеофайл, который воспроизводится после щелчка мышью на объек­
те. Для того чтобы видеофрагмент отображался корректно, в броузере должен быть
установлен соответствующий допол1П1тельный модуль.
<EMBED S R C = " i n a r t i a n - i n v a s i o n . a v i " WIDTH=120 HEIGHT=90>
Список свободно распространяемых модулей и описания связанных с ними атри­
бутов EMBED расположен по следующему адресу:
http://home.netscape.com/plugins/
Приведенные в этом списке модули поддерживают VRML, QuickTime, потоковые
аудиоданные, ф а й л ы Adobe Acrobat, изображения PNG и другие форматы данных.

Управляющие элементы ActiveX


Элемент: <OBJECT ...> ... </OBJECT>
А т р и б у т ы : CLASSID, CODETYPE, CODEBASE, ALIGN, BORDER, WIDTH, HEIGHT, HSPACE,
VSPACE, STANDBY, ARCHIVE, DATA, TYPE, NAME, TABINDEX, DECLARE, USEMAP
Internet Explorer поддерживает в составе Web-страниц управляющие элементы
ActiveX. Как и в случае Java-аплетов, для передачи параметров используются элементы
PARAM. Поскольку управляющие элементы ActiveX, в отличие от Java-аплетов, могут
выполнять любые действия в клиентской системе (в том числе удалять файлы, запус­
кать программы и т.д.), нельзя допускать, чтобы Web-клиент мог загрузить произ­
вольный элемент ActiveX. Совместно с Internet Explorer можно использовать лишь
некоторые элементы, разработанные Microsoft и независимыми производителями;
эти элементы защищаются цифровыми подписями. Многие пользователи предпочи­
тают запрещать загрузку элементов ActiveX на свои компьютеры, поэтому ActiveX в
3.6. Включение объектов в документ 105

составе Web-страниц редко применяются даже пользователями Internet Explorer. Тем


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

CLASSID
С помощью этого атрибута определяется URL. Для зарегистрированных элемен­
тов CLASSID представляется в виде c l s i d : иденшификатор_класса.

CODETYPE
Атрибут CODETYPE определяет тип объекта. Для аплетов данный атрибут задается
в виде C O D E T Y P E = " a p p l i c a t i o n / J a v a " . Дополнительные сведения приведены в
главе 9.

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

ALIGN, B O R D E R , W I D T H , H E I G H T , HSPACE и VSPACE


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

STANDBY
Атриб)а^ STANDBY задает строку текста, отображаемую при загрузке объекта.

ARCHIVE
Атрибут ARCHIVE определяет список URI ресурсов, которые должны быть загру­
жены перед активизацией объекта. Компоненты списка разделяются пробелами.
URI задаются относительно каталога, указанного с помощью атрибута CODEBASE.
Для аплетов архивы представляются в формате JAR. Для создания JAR-файла ис­
пользуется утилита j a r .

DATA и TYPE
Атрибут DATA задает URI файла данных, обрабатываемого объектом. Атрибут
TYPE определяет МШЕ-тип данных в этом файле.

NAME
Атрибут NAME обрабатывается броузерами, поддерживающими JavaScript. С помо­
щью этого атрибута задается имя, используемое JavaScript-сценарием для обраще­
ния к объекту.

TABINDEX
Если пользователь перемещается по документу с помощью клавиатуры, атрибут
TABINDEX определяет, в каком порядке элементы должны получать фокус. Значе-
106 Глава 3. Элементы текстового уровня HTML 4.0

нием TAB INDEX является целое число в диапазоне от О до 32767. П о умолчанию все
элементы, для которых не указан атрибут TAB INDEX, получают значение 0. Эле­
менты с одинаковыми значениями данного атрибута получают фокус ввода в том
порядке, в котором они расположены в документе. Данный атрибут поддержива­
ется Internet Explorer 5.x и не поддерживается Netscape 4.x.
Спецификация HTML 4.0 определяет атрибут DECLARE, который в настоящее
время не поддерживается ни Netscape, ни Internet Explorer. Этот атрибут запрещает
начальную загрузку элемента, указанного с помощью OBJECT, но разрешает загружать
его впоследствии п)тем активизации ссылки, действий, предпринимаемых другим
объектом, либо после щелчка на кнопке.
Спецификация HTML определяет также атрибут USEMAP элемента OBJECT, с по­
мощью которого задается карта изобрг1жения на стороне клиента. Атрибут USEMAP не
поддерживается Internet Explorer 5.x, Netscape 4.7 и более ранними версиями этих
броузеров, однако Netscape 6 поддерживает USEMAP.
Большое количество управляющих элементов ActiveX, разработанных независи­
мыми производителями, можно найти на сервере CNET ActiveX Download Caller)' по
адресу h t t p : / / w w w . a c t i v e x . c o m / .

Бегущая строка
Элемент: <MARQUEE ...> ... </MARQUEE>
А т р и б у т ы : WIDTH, HEIGHT, ALIGN, BEHAVIOR, BGCOLOR, DIRECTION, HSPACE, VSPACE,
LOOP, SCROLLAMOUNT, SCROLLDELAY
Элемент MARQUEE представляет собой расширение Internet Explorer. Текст, распо­
ложенный между открывающим дескриптором <^4ARQUEE . . . > и закрывающим
</MARQUEE>, оформляется в виде бегущей строки. В составе элемента MARQUEE недо­
пустимы другие элементы разметки, но сам MARQUEE может быть задан в составе таких
элементов, как FONT. В результате автор получает возможность создавать, например,
бегущую строку, в которой отображаются символы большого размера синего цвета. Все
броузеры, кроме Internet Explorer, игнорируют дескрипторы MARQUEE и рассматривают
текст, содержащийся в составе данного элемента, как часть текущего абзаца. Элемент
MARQUEE можно использовать без атрибутов либо включать в состав открывающего де­
скриптора атрибуты, описанные ниже. Заметьте, что многие пользователи рассматри­
вают бегущую строку как досадную помеху, затрудняющ)то восприятие Web-страницы,
поэтому, используя элемент MARQUEE, соблюдайте осторожность.

WIDTH и HEIGHT
Данные атрибуты задают высоту и ширину области для бегущей строки. З н а ч е н и я
атрибутов указываются в пикселях либо в процентах от размеров окна Internet
Explorer. П о умолчанию принимается ширина области, равная ширине окна, а вы­
сота выбирается в зависимости от используемого шрифта.
3.6. Включение объектов в документ 107

ALIGN
Атрибут ALIGN определяет выравнивание бегущей строки относительно окру­
жающего текста. Допустимыми значениями являются LEFT, RIGHT, CENTER, TOP,
BOTTOM и MIDDLE; они используются так же, как и соответствующие значения эле­
мента IMG, описанные ранее в этой главе.

BEHAVIOR
Атрибут BEHAVIOR описывает порядок перемещения текста. Значение SCROLL,
принимаемое по умолчанию, указывает, что текст должен перемещаться в одном
направлении до тех пор, пока не достигнет края области, после чего движение
текста начинается сначала. Если задано значение SLIDE, текст перемещается до
тех пор, пока не достигнет края области, после чего движение прекращается. Зна­
чение BOUNCE указывает на то, что текст должен перемещаться вперед и назад в
пределах области.

BGCOLOR
Атрибут BGCOLOR задает цвет ф о н а области бегущей строки.

DIRECTION
Атрибут DIRECTION задает направление, в котором должен перемещаться текст
(если атрибут BEHAVIOR имеет значение BOUNCE, то атрибут DIRECTION указыва­
ет, в каком направлении текст начнет движение). Допустимыми значениями явля­
ются LEFT (перемещение слева направо) и RIGHT (перемещение справа налево).
По умолчанию принимается значение LEFT.

HSPACE и VSPACE
Атрибуты HSPACE и VSPACE задают размеры пустого пространства вокруг области
бегущей строки. Значения данных атрибутов указываются в пикселях.

LOOP
Атрибут LOOP указывает, сколько раз должно повторяться перемещение текста.
Значение, равное -1, или INFINITE (принимаемое по умолчанию) указывает на то,
что текст должен перемещаться все время, пока дoк)^vIeнт отображается в окне
броузера.

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

SCROLLDELAY
Атрибут SCROLLDELAY задает время в миллисекундах между последовательными
показами текста.
108 Глава 3. Элементы текстового уровня HTML 4.0

3.7. Управление переводом строк


Чаще всего броузер расставляет переводы строк так, чтобы при выводе текста ис­
пользовалось все доступное пространство. В главе 2 был рассмотрен элемент PRE, ко­
т о р ы й задает отображение текста символами фиксированной ш и р и н ы и сохраняет
переводы строк, указанные в исходном тексте. В языке HTML существует также эле­
мент BR, который позволяет явно задавать переводы строк. Кроме того, запретить
перевод строки в конкретной позиции текста можно, разделяя слова неразрывным
пробелом либо применяя нестандартный, но часто используемый элемент NOBR.

Элемент: <BR ...> (закрывающий дескриптор отсутствует)


Атрибут: CLEAR
Элемент BR реализует перевод строки, не завершая текущий абзац. Поскольку в
исходном HTML-тексте (зз. исключением некоторых элементов) переводы строк иг­
норируются, элемент BR необходим для того, чтобы разместить текст на экране тре­
буемым образом. Атрибут CLEAR, допустимыми значениями которого являются NONE
(принимается по умолчанию), LEFT, RIGHT и ALL, позволяет запретить обтекание
изображения текстом. П р и работе со многими броузерами, включая Netscape и
Internet Explorer, несколько последовательно расположенных дескрипторов <BR>
приводят к отображению дополнительных пустых строк (несколько последовательно
расположенных дескрипторов <Р> не позволяют добиться подобного результата).

Элемент: <NOBR>... </NOBR>


Атрибуты: отсутствуют
Элемент NOBR не является частью спецификации HTML 4.0, но по.адерживается
Netscape и Internet Explorer. NOBR подавляет перенос текста, содержащегося в его со­
ставе, на новую строку, за исключением позиций, отмеченных дескриптором <WBR>.
Аналогичного эффекта можно добиться, разделяя неразрывным пробелом ( & n b s p ; )
слова, которые желательно отобразить в одной строке.

Элемент: <WBR> (закрывающий дескриптор отсутствует)


Атрибуты: отсутствуют
Элемент WBR может встречаться только в составе элемента NOBR. WBR помечает по­
зицию, в которой при необходимости можно осуществить разрыв строки. WBR не
предусмотрен в спецификации HTML 4.0, но, как правило, поддерживается теми бро­
узерами, которые поддерживают элемент NOBR.

3.8. Резюме
Элементы текстового уровня дают возможность определять стили отображения,
задавать переводы строк и создавать гипертекстовые ссылки в составе документа.
Кроме того, с помощью элементов текстового уровня можно помещать на Web-
страницу изображения, оформлять их как гипертекстовые ссылки и даже связывать с
различными областями одного изображения разные ссылки. Многие броузеры позво-
3.7. Управление переводом строк 109

ляют также включать в состав документа Java-аплеты, аудио- и видеофрагменты, фай­


лы Adobe Acrobat, компоненты VRML и управляющие элементы ActiveX.
В последующих двух главах описаны дополнительные возможности, которые
можно использовать при создании документов. В главе 4 рассматриваются ф р е й м ы , с
помощью которых можно разделить Web-страницу на ряд прямоугольных областей и
отобразить в каждой из них отдельный HTML-документ. Глава 5 посвящена каскад­
ным листам стилей, предоставляющим мощные средства для управления шрифтами,
размещением текста и стилями в элементах блокового и текстового уровней.
ФРЕЙМЫ

в этой главе...

Структура документов с фреймами.


Разделение окна броузера на области.
Определение содержимого фреймов.
Связывание гипертекстовых ссылок с фреймами.
Использование предопределенных имен фреймов.
Решение проблем, связанных с использованием фреймов.
Использование внутренних (плавающих) фреймов.
J~y\:EJ^zJ

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

Ф угольной ф о р м ы и отобразить в каждой из них отдельный HTML-документ.


Механизм ф р е й м о в был первоначально разработан Netscape Corporation для
применения в броузере Navigator 2.0, а затем адаптирован Microsoft Corporation для
Internet Explorer 3.0. Несмотря на то что ф р е й м ы не были предусмотрены специфи­
кацией HTML 3.2, их популярность среди разработчиков Web-документов привела к
тому, что они стали частью HTML 4.0. Хотя ф р е й м ы предоставляют возможности,
которые не могут обеспечить другие средства HTML, вокруг них до сих пор вед)а'ся
споры, поскольку наряду с бесспорными преимуществами они имеют некоторые не­
достатки. Основные преихмущества использования ф р е й м о в перечислены ниже.

• Автор может гарантировать, что определенные компоненты и н т е р ф е й с а


(например, содержание документа) всегда будут присутствовать на экране.
• Фреймы дают возможность избежать п о в т о р е н и я одних и тех же управляющих
разделов на различных Web-страницах. Вместо этого один документ может
отображаться как ф р е й м в составе разных Web-страниц.
• При использовании фреймов упрощается перемещение по большим Web-узлам,
• С помощью ф р е й м о в удобно объединять текстовые HTML-документы с Java-
аплетами.
• Пользоваться картами изображений гораздо удобнее в случае, если карта ото­
бражается в одном фрейме, а в другом ф р е й м е выводятся документы, получен­
ные вследствие активизации ее областей.
Этими преимуществами обусловлена популярность ф р е й м о в среди авторов Web-
страниц. Однако их применение имеет н е к о т о р ы е недостатки.
112 Глава 4. Фреймы

• Если Web-страница включает ф р е й м ы , пользователь испытывает трудности


при работе с кнопками BACK и FORWARD.
• Плохо продуманная структура ф р е й м о в затрудняет навигацию пользователя по
Web-страницам.
• Поскольку в строке ввода адреса представляется URL документа верхнего
уровня, пользователю трудно определить URL документа, отображающегося в
конкретной области.
• Поскольку закладки содержат URL конкретных документов, пользователь не
может сохранить текущую конфигурацию содержимого ф р е й м о в (т.е. внешний
вид Web-страницы после выполнения нескольких действий с управляющими
элементами фреймов).
• П р и активизации пункта меню Print (или соответствующей кнопки) броузер
выводит на печать содержимое активного фрейма. В большинстве случаев ак­
тивным оказывается ф р е й м , в котором выводится содержание.
П о этой причине некоторые пользователи не признают ф р е й м о в и предпочитают
те Web-страницы, в которых данный механизм не применяется. Несмотря на то что
материал, изложенный в данной главе, позволит вам избежать проявления некото­
рых недостатков фреймов, лучше всего ограничить их использование теми ситуация­
ми, когда выгода от применения ф р е й м о в настолько очевидна, что можно смириться
с потерей некоторой части пользователей.
Спецификация HTML 4.0 определяет атрибут TITLE для всех элементов данной
главы. Однако поскольку этот атрибут был описан в главе 1, то здесь он рассматри­
ваться не будет. Кроме того, спецификация каскадных листов стилей также опреде­
ляет для всех элементов, рассматриваемых в данной главе, атрибуты CLASS, ID и
STYLE. Обсуждение этих атрибутов мы отложим до главы 5,

4 . 1 . Шаблон документа о фреймами


В обычном HTML-документе за разделом HEAD непосредственно следует раздел
BODY, в котором содержится и н ф о р м а ц и я , предназначенная для отображения. В до­
кументе с фреймами элемент BODY либо отсутствует, либо расположен в разделе
NOFRAMES, предназначенном для броузеров, которые не поддерживают фреймы.
Вместо элемента BODY используется элемент FRAMESET, с помощью которого задается
разбиение окна броузера на столбцы или строки. Т и п и ч н ы й шаблон документа с
фреймами представлен в листинге 4.1. В состав FRAMESET входят другие элементы
FRAMESET, реализующие дальнейшее разбиение окна, либо элементы FRAME, опреде­
ляющие URL документов, которые должны отображаться внутри ф р е й м о в . Документ
с фреймами должен включать соответствующую декларацию DOCTYPE.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Frameset//EN">
Это необходимо для проверки корректности д о к \ ^ е н т а .
4 . 2 . Р а з б и е н и е окна броузера на ф р е й м ы 113

Листинг 4 . 1 . Шаблон документа с фреймами

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Frameset//EN">


<HTML>
<HEAD>
<TITLE>Document T i t l e < / T I T L E >
</HEAD>
<FRAMESET . . . >
<!— FRAME and N e s t e d FRAMESET E n t r i e s —>
<NOFRAMES>
<BODY>
< ! - - S t u f f f o r n o n - f r a m e s b r o w s e r s —>
</BODY>
</NOFRAMES>
</FRAMESET>
</HTML>

4.2. Разбиение окна броузера на фреймы


Элемент FRAMESET определяет число ф р е й м о в и их размеры.

Элемент: <FRAMESET> ... </FRAMESET>


Атрибуты: ROWS, COLS, FRAMEBORDER (нестандартный), BORDER (нестандартный),
FRAME SPACING (нестандартный), BORDERCOLOR (нестандартный), ONFOCUS (нестандарт­
ный), ONBLUR (нестандартный), ONLOAD, ONUNLOAD
Элемент FRAMESET делит окно броузера или ф р е й м на строки или столбцы. По­
скольку элементы FRAMESET допускают вложенность, автор может реализовать слож­
ную структ}ру фреймов. Основными атрибутами элемента FRAMESET являются ROWS и
COLS. Атриб)ты FRAMEBORDER, BORDER, BORDERCOLOR, ONFOCUS и ONBLUR- нестан­
дартные, однако они поддерживаются Netscape 4.x, Internet Explorer 4.x и более
поздними версиями этих броу.зеров. FRAME SPACING также является нестандартным
атриб)пгом; он поддерживается Internet Explorer. Атрибуты ONLOAD и ONUNLOAD зада­
ют JavaScript-код, выполняемый соответственно при загрузке и прекращении отобра­
жения набора фреймов.

ROWS
Атрибут ROWS делит окно броузера (или текущий фрейм — в случае вложенных
элементов FRAMESET) на горизонтальные строки. Этот атрибут используется сле­
дующим образом:
<FRAMESET ROWS="Rowl-Size, ... , RowN-Size">

</FRAMESET>
Приведенный выше фрагмент кода делит окно броузера на N строк. Размер строки
задается целым числом (в этом случае значение интерпретируется как количество
пикселей), либо целым числом, за которым след}^ет символ "%" (в этом случае раз­
мер строки вычисляется в процентах от размера допустимой области), либо сим­
волом "*". Знак "*" интерпретируется как "оставшееся пространство"; при необхо-
114 Глава 4 . Ф р е й м ы

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


ния на строки приведен ниже.
<FRAMESET ROWS="50,10%,*f2*">

</FRAMESET>
Данный фрагмент кода задает в окне четыре строки. Высота первой строки равна
50 пикселям. Вторая строка занимает 10 процентов от высоты допустимой облас­
ти. Оставшееся пространство распределяется между двумя строками, причем тре­
тья строка занимает одну треть этого пространства, а четвертая — две трети (так
как перед знаком "*" был указан множитель 2). Д а н н о е деление окна броузера по­
казано на рис. 4.1.

1Щ Ffаяю«е1 ROV/S АК/^Ь^Ш^ЫтляШ inimn^ Шфтт ' mRGi


""^"""|gi||

1 Frame Cell 4
1 ТП i^-^ 11 Щ
Frame Cell Я

Frame Cell
1 This is a sample HTML document to be used in frame cells.

Щ^Щ^

Рис. 4.1. Документ с фреймами, в котором размеры строк


заданы как " 5 0 , 10%, * , 2 * "

Как видно из рисунка, малые размеры ф р е й м а приводят к том)% что документ, ото­
бражаемый в нем, практически невозможно читать. Планируя деление окна,
имейте в виду, что размеры окна и ш р и ф т на компьютере пользователя могут от­
личаться от выбранных вами при просмотре документа. Заметьте, что значение
атрибута ROWS должно определять как минимум две строки, в противном случае в
броузере Netscape Web-страница будет отображаться некорректно. Например,
приведенный ниже фрагмент кода будет выведен как пустое окно (при использо­
вании Netscape).
<FRAMESET R O W S - " * " >
<FRAME SRC="CoreWebProgramming.html">
</FRAMESET>

Внимание!

С помощью элемента FRAMESET следует задавать на меньше двух


строк или столбцов.
4 . 2 . Разбиение окна броузера на фреймы 115

COLS
Атрибут COLS делит окно броузера (или текущий фрейм— в случае вложенных
элементов FRAMESET) на вертикальные столбцы. Как и ROWS, атрибут COLS может
присутствовать либо в документе верхнего уровня, либо в составе фрейма, опре­
деленного посредством FRAMESET. Д а н н ы й атрибут должен определять как мини­
мум два столбца. Помещать в состав FRAMESET один элемент FRAME нет необходи­
мости, кроме того, это может привести к некорректному отображению Web-
страницы в броузере Netscape. В составе открывающего дескриптора FRAMESET
должен быть указан либо один атрибут ROWS, либо один атрибут COLS.

FIIAMEBORDER
Атрибут FRAMEBORDER определяет, должны ли отображаться разделительные ли­
нии между фреймами. П о умолчанию разделительные л и н и и выводятся на экран.
Значение FRAMEBORDER=0 о б ы ч н о применяется в сочетании со значениями
BORDER=0 и FRAMESPACING=0, результатом являются ф р е й м ы без внутренних и
внешних разделителей. З н а ч е н и е FRAMEBORDER элемента FRAMESET заменяется
значением FRAMEBORDER элемента FRAME либо значением этого атрибута в составе
вложенного FRAMESET. Netscape 3.0, Internet Explorer 4.0, a также их более новые
версии поддерживают значение атрибута FRAMEBORDER YES, или 1, указывающее
на то, что разделители должны отображаться, и значение N0, или О, запрещающее
вывод разделителей. В качестве примера в листингах 4.2 и 4.3 приведены исход­
ные коды двух документов с фреймами. Эти документы почти идентичны и отли­
чаются только использованием атрибута FRAMEBORDER. Результаты показаны на
рис. 4.2 и 4.3.

Листинг4.2. Frame-Borders.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 F r a i n e s e t / / E N " >


<HTML>
<HEAD>
<TITLE>Frames w i t h B o r d e r s < / T I T L E >
</HEAD>
<FRAMESET ROWS="40%,60%">
<FRAME S R C = " F r a m e - C e l l . h t m l " >
<FRAMESET COLS="*,*">
<FRAME S R C = " F r a m e - C e l l . h t m l " >
<FRAME S R C = " F r a m e - C e l l . h t m l " >
</FRAMESET>
<NOFRAMES>
<BODY>
Your b r o w s e r d o e s n o t s u p p o r t f r a m e s . P l e a s e s e e
<A H R E F = " F r a m e - C e l l . h t m l " > n o n f r a m e s v e r s i o n < / A > .
</BODY>
</NOFRAMES>
</FRAMESET>
</HTML>
116 Глава 4. Фреймы

:LJjмл^li'^lJi^J!•PL^'Ш'JШl^Jli!lИl>?fЯlf^^ '
£de £d« View F§vofkes lools й«Ь ^ > ^ ., ^ КШ[

Frame Cell
1 Tbs IS a sample HTML document to be used ш frame cells. 1

Frame Cell Frame Cell


1 Tbs IS a sample HTML document to be This is a sample HTML document to be 1
1 used in frame cells. used in frame cells. |

0] Done i ^ My Cofflpii»

Рис. 4.2. По умолчанию между фреймами выводятся разде­


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

Листинг4.3. Frame-Borderless.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
<HTML>
<HEAD>
<TITLE>Borderless Frames</TITLE>
</HEAD>

<FRAMESET ROWS="40%, 60%" FIU^MEBORDER=0


BORDER=0 FRAMESРАСING=0>
<FRAME SRC="Frame-Cell.html">

<FRAMESET COLS="*,*">
<FRAME SRC="Frame-Cell.html">
<FRAME SRC="Frame-Cell.html">
</FRAMESET>
<N0FRAMES>
<BODY>
Your browser does not support frames. Please see
<A HREF="Frame-Cell.html">nonframes version</A>.
</BODY>
</NOFRAMES>
</FRAMESET>

</HTML>
4 . 2 . Разбиение окна броузера на фреймы 117

I^Borderlet* Ftamex - Mictos<rfi fntetftet ExfiltMrei шшшШ


; £йе £dft View Favorites Tods Ц^Ь
Bi^
i^ • - J J ^ ^ : i i J -ii' ^_^'

Frame Cell
1 This IS a sample HTML document to be iised m frame cells.

Frame Cell Frame Cell


1 This is a sample HTML document to be This IS a sample HTML document to be |
1 used in frame cells. used in frame cells.

^;^Шпё''"":;- ' f" • •' Щщ^от^мы':

Рис. 4.3. Вывод разделителей между фреймами запрещает­


ся посредством атрибута FRAMEORDER

BORDER и FRAMESPACING (Internet Explorer)


Данные атрибуты, специфические для Internet Explorer, задают толщину раздели­
тельных линий между фреймами. О н и применяются только к внешнему элементу
FRAMESET. Значение по умолчанию равно 5.

BORDERCOLOR
BORDERCOLOR— нестандартный атрибут, поддерживаемый как Netscape, так и
Internet Explorer. Он задает цвет разделителя между фреймами. В качестве значе­
ния задается либо шестнадцатеричное число, определяющее цвет, либо имя цвета.
По )ТУ1олчанию используются цвет ф о н а с уменьшенной яркостью и имитация
трехмерных э ф ф е к т о в посредством затенения. Значение этого атрибута замеща­
ется значением BORDERCOLOR элемента FRAME либо вложенного элемента
FRAMESET.

ONFOCUS и O N B L U R
Эти атрибуты задают код JavaScript, выполняемый в тот момент, когда ф р е й м со­
ответственно получает или теряет фокус. Несмотря на то что имена атрибутов не­
чувствительны к регистру символов, данные атрибуты связаны с выполнением
JavaScript-сценариев, поэтом)^ их принято обозначать o n F o c u s и o n B l u r .

ONLOAD и O N U N L O A D
Данные атрибуты определяют JavaScript-код, который выполняется при загрузке
набора ф р е й м о в и тогда, когда броузер прекращает их отображение. Эти атрибуты
также принято обозначать o n L o a d и o n U n l o a d .
118 Глава 4 . Ф р е й м ы

4.3. Определение содержимого фреймов


Элемент: <FRAME SRC="..." ...> (закрывающий дескриптор отсутствует)
Атрибуты: SRC, NAME, FRAMEBORDER, BORDERCOLOR (нестандартный), MARGINWIDTH,
MARGINHEIGHT, SCROLLING, NORESIZE, LONGDESC
Элемент FRAME определяет HTML-документ, который должен отображаться в
конкретном фрейме. Элементы FRAME могут присутствовать лишь в составе контей­
неров FRAMESET. BORDERCOLOR— нестандартный атрибут, поддерживаемый как
Netscape, так и Internet Explorer.

SRC
Атрибут SRC задает URL документа, который должен быть отобрг1жен в текущем
фрейме. Этот атрибут не является обязательным, поскольку спецификация HTML
4.0 допускает пустые ф р е й м ы . Однако во избежание непредсказуемых результатов
при работе с Netscape атрибут SRC желательно задавать.
Внимание!

Элемент FRAME, ДЛЯ которого задан атрибут NAME, НО не указан SRC,


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

NAME
Атрибут NAME присваивает имя текущему фрейму. Атрибут TARGET элементов А,
AREA, BASE и FORM может быть использован для отобра>1се1П1Я во ф р е й м е нового
документа. К тому же результат)^ приведет вызов Java-метода showDocument, для
которого вторым параметром задано имя фрейма. Имя должно начинаться с бук­
вы, однако существуют четыре заранее определенных имени, начинающиеся с
символа подчеркивания.

FRAMEBORDER
Атрибут FRAMEBORDER определяет, должны ли отображаться разделители с ими­
тацией трехмерного представления. З н а ч е н и е FRAMEBORDER элемента FRAME пе­
реопределяет значение одноименного атрибута, который принадлежит включаю­
щему элементу FRAMESET. Считается, что разделитель не должен отображаться
только в том случае, если в двух смежных фреймах его вывод запрещен соответст­
вующими значениями FRAMEBORDER. Netscape 3.0, Internet Explorer 4.0 и более но­
вые версии этих броузеров поддерживают значение атрибута FRAMEBORDER YES,
или 1, указывающее на то, что разделители должны отображаться, и значение N0,
или О, запрещающее вывод разделителей!.

BORDERCOLOR
Атрибут BORDERCOLOR задает цвет разделителя между фреймами. В качестве зна­
чения задается либо шестнадцатеричное число, определяющее цвет в ф о р м а т е
RGB, либо имя цвета (см. табл. 1.1 в разделе 1.6). В случае конфликта цвета прини­
мается цвет вложенного элемента.
4.3. Определение содержимого фреймов 119

MARGINWIDTH
Атрибут MARGINWIDTH определяет размеры левой и правой границ.

MARGINHEIGHT
Атрибут MARGINHEIGHT определяет размеры верхней и нижней границ.

SCROLLING
Атрибут SCROLLING определяет, должна ли в составе ф р е й м а отображаться полоса
прокрутки. В Netscape значение YES указывает на то, что полоса прокрутки всегда
должна присутствовать в составе фрейма. Если задано значение AUTO (принимает­
ся по умолчанию), то полоса прокрутки отображается только в том случае, если
документ не помещается в отведенном для него пространстве. Для Internet Ex­
plorer YES (значение по умолчанию) означает то же, что и AUTO. Значение N0 за­
прещает вывод полосы прокрутки как в Netscape, так и в Internet Explorer.

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

LONGDESC
Атрибут LONGDESC задает URI документа, в котором приводится детальное описа­
ние фрейма. О б ы ч н о LONGDESC применяется для невизуальных броузеров.

Элемент: <NOFRAMES> ... < / N O F R A M E S >


Атрибуты: отсутствуют
Броузер, поддерживающий ф р е й м ы , игнорирует текст, который содержится в со­
ставе контейнера NOFRAMES. П р о ч и е броузеры не обрабатывают дескрипторы
<NOFRAMES> и </NOFRAMES>, однако текст и элементы разметки, находящиеся между
этими дескрипторами, интерпретируются как содержимое обычного документа. В ре­
зультате автор получает возможность реализовать версию Web-страницы без фрей­
мов либо предоставить пользователю ссылки на несколько основных документов.
Приведенный ниже п р и м е р не дает пользователю никакой полезной информации о
документе, но демонстрирует возможности элемента NOFRAMES.
<NOFRAMES>
Your b r o w s e r d o e s n ' t s u p p o r t frames.
Get a < B > r e a l < / B > b r o w s e r .
</NOFRAMES>
Спецификация HTML 4.0 позволяет использовать в составе дескриптора NOFRAMES
атрибуты, предназначенные для управления событиями (ONCLICK, ONDBLCLICK,
0NKEYDOWN, ONKEYPRESS, ONKEYUP, ONMOUSEDOWN, ONMOUSEMOVE, ONMOUSEOUT,
ONMOUSEOVER, ONMOUSEUP). Подробная информация об обработке событий JavaScript-
программами содержится в главе 24. Спецификация HTML 4.0 также определяет для
элемента NOFRAMES атрибуты DIR и LANG (они были описаны в разделе L6).
120 Глава 4. Фреймы

Примеры
Код, приведенный в листинге 4.4, создает в окне броузера две строки; первая
строка делится на три столбца, а вторая — на два. Результат показан на рис. 4.4.

Л и с т и н г 4 . 4 . Frame-Examplel.htinl

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">


<HTML>
<HEAD>
<TITLE>Frame Example 1</TITLE>
</HEAD>
<FRAMESET ROWS="55%,45%">
<FRAMESET COLS="*,^,*">
<FRAME SRC="Frame-Cell.html">
<FRAME SRC="Frame-Cell.html">
<FRAME SRC="Frame-Cell.html">
</FRAMESET>
<FRAMESET COLS="*,*">
<FRAME SRC="Frame-Cell.html">
<FRAME SRC="Frame-Cell.html">
</FRAMESET>
<NOFRAMES>
<BODY>
Your browser does not support frames. Please see
<A HREF="Frame-Cell.html">nonframes version</A>.
</BODY>
</NOFRAMES>
</FRAMESET>
</HTML>

\Щ Flame Exairade 1 • Netscape


Re £<4( View go jQommunicdJof He^

'i 4 i^' Д r?l ^-^ :й -i^ ^ 3 a


Frame Cell Frame Cell Frame Cell
This IS a sample HTML This IS a sample HTML This IS a sample HTML
document to be used in frame document to be used in document to be used in frame
ceUs. frame cells cells

Frame Cell Frame Cell


This IS a sample HTML document to be used ш This IS a sample HTML document to be used in
frame cells. frame cells.

.:^-:^...m..
Рис. 4.4. Окно броузера разделено на две строки; в первой из них
созданы три столбца, а во второй — два
4.3. Определение содержимого фреймов 121

Код, представленный в листинге 4.5, делит окно на два столбца. В первом столбце
создаются три строки, а во втором — две. Результат показан на рис. 4.5.

Листинг 4.5. Frame-Example2 .html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">


<HTML>
<HEAD>
<TITLE>Frame Example 2</TITLE>
</HEAD>
<FRAMESET COLS="55%,45%">
<FRAMESET ROWS="*,*,*">
<FRAME SRC="Frame-Cell.html">
<FRAME SRC="Frame-Cell.html">
<FRAME SRC="Frame-Cell.html">
</FRAMESET>
<FRAMESET ROWS="*,*">
<FRAME SRC="Frame-Cell.html">
<FRAME SRC="Frame-Cell.html">
</FRAMESET>
<NOFRAMES>
<BODY>
Your browser does not support frames. Please see
<A HREF="Frame-Cell.html">nonframes version</A>.
</BODY>
</NOFRAMES>
</FRAMESET>
</HTML>

^ Fiaee EiMMiple г * Netsoane


fie £сЙ View fio ^omrriurticaloi |ie^

^.~ ^ ^ a Ш Ш
Frame Cell Frame Cell
This IS a sample HTML document to be used in This IS a sample HTML document to be
frame ceDs. used in frame cells.

Frame Cell
This is a sample HTML document.to be used in
frame cells. Frame Cell
This is a sample HTML document to be
Frame Cell used in frame cells.

This is a sample HTML document to be used in


frame cells.

i j ^ ri»»^J>« : DoctmwrA: Don* jys- „ ^ ^ ^ , L 3 ..••^:.

Рис. 4.5. Окно броузера разделено на два столбца. В первом столб­


це созданы три строки, а во втором — две
122 Глава 4. Фреймы

4.4. Определение фрейма для отображения


документа
Автор Web-страницы может создать ее так, чтобы при активизации ссылки доку­
мент, на который она указывает, отображался в конкретном, заранее заданном фрей­
ме. Чтобы сделать это, надо присвоить требуемому фрейму имя, а в состав гипертек­
стовой ссылки включить атрибут TARGET, указав в качестве значения этого атрибута
имя фрейма. Заметьте, что имена, определяемые пользователями, не должны начи­
наться со знака подчеркивания. П р и отсутствии атрибута TARGET документ, соответ­
ствующий активизированной ссылке, отображается в том ф р е й м е , в котором нахо­
дится эта ссылка. Если в атрибуте TARGET вы укажете имя несуществующего фрейма,
документ будет открыт в новом окне броузера и этому окну будет присвоено имя, за­
данное в качестве значения TARGET. Атрибут TARGET может присутствовать в элемен­
тах А, AREA, BASE и FORM. Java-аплеты могут обращаться к именованным фреймам, за­
давая имя как второй параметр метода g e t A p p l e t C o n t e x t () . s h o w D o c u m e n t .

М е т о д и к а профессионалов

Не присваивайте фреймам имена, начинающиеся со знака подчер­


кивания.

Часто ф р е й м ы используются для создания небольшой панели инструментов или


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

ШЕШШ2ШШШШ1 щшт
£1е £ Л Yiew fio Cownwiicala ЦеЬ

- ^^ ^ Ш fi^ ^^ ^
TntTOfhirtio» Tnvpsring

Introduction
А new breakthrough m cold fusion technology!

A ripe opportunity for the Kicky investor!

Accepted by the scientific community! Documented in The Journal of Irreproducible Results

'OocumentDone :, v;^ •'м, 'Л--^. Г?1 л*^

Рис. 4.6. Web-страница, содержащая два фрейма: таблицу с


содержанием и основной фрейм, в котором отображается раздел
документа
4.4. Определение фрейма для отображения документа 123

Web-страница создается так, чтобы под таблицу с содержанием отводилось про­


странство фиксированного размера. Остальная часть окна предназначается для вы­
вода док)ТУ1ента (имя этого ф р е й м а Main). В листинге 4.6 представлен HTML-код до­
кумента верхнего уровня.

Листинг4.6. Cold--Fusion.html

<!DOCTYPE xHTML PUBLIC "-//W3C//DTD HTML 4 . 0 F r a m e s e t / / E N " >


<HTML>
<HEAD>
< T I T L E > I n v e s t i n g i n Cold F u s i o n < / T I T L E >
</HEAD>
<FRAMESET ROWS="75,*">
<FRAME SRC="TOC.html" NAME="TOC">
<FRAME S R C = " I n t r o d u c t i o n . h t m l " NAME="Main">
<NOFRAMES>
<BODY>
T h i s p a g e r e q u i r e s F r a m e s . For a n o n - F r a m e s v e r s i o n ,
<A H R E F = " I n t r o d u c t i o n . h t m l " > t h e i n t r o d u c t i o n < / A > .
</BODY>
</NOFRAMES>
</FRAMESET>
</HTML>

Каждая из ссылок в д о к ) ^ е н т е ТОС. h t m l содержит атрибут TARGET, для которого


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

Листинг 4 . 7 . ТОС.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>Table of C o n t e n t s < / T I T L E >
</HEAD>
<BODY>
<TABLE WIDTH="100%">
<TR><TH><A H R E F = " I n t r o d u c t i o n . h t m l " TARGET="Main">
Introduction</A></TH>
<TH><A H R E F = " P o t e n t i a l . h t m l " TARGET="Main">
Potent ial</AX/TH>
<TH><A H R E F = " I n v e s t i n g . h t m l " TARGET="Main">
Investing</A></TH>
<TH><A HREF="References.html" TARGET="Main">
References</A></TH></TR>
</TABLE>
</BODY>
</HTML>

П р и выборе ссылки в таблице содержимое верхнего фрейма остается неизмен­


ным. Того же э ф ф е к т а можно достичь, вовсе отказавшись от атрибутов TARGET в со-
124 Глава 4 . Ф р е й м ы

ставе гипертекстовых ссылок и включив в раздел HEAD выражение <BASE


TARGET="Main">. На рис. 4.7 показано состояние окна после выбора ссылки
I n v e s t i n g . Документ, содержащий атрибут TARGET, не считается строго соответст­
вующим HTML 4.0, поэтому в начало этого документа должна быть включена проме­
жуточная декларация DOCTYPE. Очевидно, что четыре документа, на которые указы­
вают ссылки в ТОС. h t m l , должны соответствовать HTML 4.0 и не должны содержать
атрибуты TARGET, поскольку при активизации ссылки без этого атрибута документ
выводится в текущем фрейме.
М е т о д и к а профессионалов _

Если все ссылки на Web-странице должны выводить документы в


одном и том же фрейме, то указать целевой фрейм можно, включив
в раздел HEAD выражение <BASE TARGET=". . . ">.

Зарезервированные имена фреймов


в качестве значения атрибута TARGET можно задавать одно из четырех встроенных
имен: b l a n k , t o p , p a r e n t и s e l f . Поскольку имена фреймов, определенные
пользователем, не могут начинаться со знака подчеркивания, указанные выше имена
интерпретир)тотся одинаково для всех фреймов док)ТУ1ента. В табл. 4.1 перечислены
действия гипертекстовых ссылок, связанные с зарезервированными именами.

Таблица 4 . 1 . Зарезервированные имена ф р е й м о в

Нмя Дейапвие
_Ь1апк Если в гипертекстовой ссылке в качестве целевого имени указывается
имя b l a n k , документ, на который указывается ссылка, отображается
в новом неименованном окне
_top Использование t o p в качестве целевого имени приводит к тому, что
документ, на к о т о р ы й указывает ссылка, занимает все окно броузера.
Несмотря на т о что такой документ не включается в конкретный
фрейм, считается, что Web-страница содержит ф р е й м ы
_parent Если в качестве целевого имени указано _ p a r e n t , документ распола­
гается в области, занимаемой элементом FRAMESET, родительским по
отношению к данному документу. Если Web-страница не содержит
вложенных ф р е й м о в , то использование имени _ p a r e n t приводит к
тому же результат)^ что и использование имени _ t o p
_self Если в гипертекстовой ссылке в качестве целевого имени указывается
имя s e l f , документ отображается в текущем ф р е й м е . Присутствие
_ s e l f необходимо только в том случае, если это имя переопределяет
целевой ф р е й м , заданные! с помощью элемента BASE. Если выраже­
ние <BASE TARGET="имя_фpeймa"> не задано, т о по умолчанию до­
кумент, на к о т о р ы й указывает ссылка, выводится во ф р е й м е , содер­
жащем эту ссылку
4.5. Разрешение типичных проблем... 125

4.5. Разрешение типичных проблем,


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

Создание закладок для фреймов


Предположим, что вы просматриваете Web-страницу, содержащую ф р е й м ы . Назо­
вем ее U r l l . Некоторое время вы работали с этой страницей и, переходя по ссылкам,
нашли интересующий вас документ. Назовем его U r l 2 . Предположим также, что вы
создали закладку на U r l 2 . Если через некоторое время вы выберете созданную за­
кладку, броузер вместо требуемого документа ( U r l 2 ) отобразит исходную страницу с
фреймами ( U r l l ) . Проблема заключается в том, что при создании закладки с помо­
щью меню Boormarks или Favohties (в зависимости от броузера) сохраняется не URL
документа, отображаемого в выбранном вами фрейме, а URL документа верхнего
уровня. Иногда такой результат выбора ссылки оказывается полной неожиданностью
для пользователя. Если разделители не отображаются, пользователь может даже не
знать, что он имеет дело с фреймами. Об этом можно догадаться по URL, показанно­
му в строке ввода адресов, но при работе на него редко обращают внимание.
Броузеры Netscape и Internet Explorer, работающие в системе Windows, после
щелчка правой кнопкой мыши отображают контекстное меню. Один из пунктов этого
меню позволяет создать закладку на документ, отображаемый в конкретном ф р е й м е .
Однако впоследствии при выборе этой закладки в окне броузера отобрг1жается не вся
структура фреймов, а один документ.
Пользователи должны знать, как создавать закладки на документы, отображаемые
в отдельных фреймах. В свою очередь, разработчики Web-страниц с фреймами долж­
ны иметь в виду, что при создании закладок у пользователей часто возникают про­
блемы. Во-первых, необходимо хорошо продумать взаимосвязь между документами,
чтобы пользователь, который создал закладку на документ верхнего уровня, мог бы­
стро найти требуемые данные. Во-вторых, для страниц, на которые ожидается боль­
шое количество ссылок, надо включить в тело документа URL в текстовом виде. В-
третьих, желательно создать версию Web-страницы без фреймов, содержащую ссыл­
ки на все требуемые документы. Если содержимое документов часто меняется, версию
без ф р е й м о в поддерживать трудно. В этом случае можно создавать ссылки, с помо­
щью которых документы отображались бы, полностью занимая окно броузера. При­
мер такой ссылки приведен ниже.
<А HREF="http://some-site.com/some-page.html TARGET="_top">
http://some-site.com/some-page.html</A>.
126 Глава 4 . Ф р е й м ы

Вывод фреймов на печать


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

Совет

Перед тем как выводить на печать часть документа с фреймами, не­


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

В Internet Explorer 5.x вывод фрейма на печать происходит намного проще. После
выбора пункта Print меню File броузер предложит вам следующие действия на выбор:
• печать всех ф р е й м о в так, как они отображаются на экране;
• печать только выбранного фрейма;
• печать каждого ф р е й м а по отдельности.
Выбрав нужную опцию, вы можете вывести документ на печать в требуемом виде.
Для выбранных ф р е й м о в Internet Explorer 5.x поддерживает дополнительные опции
печати. Вы можете задать вывод на п р и н т е р всех связанных документов (при выборе
этой опции надо соблюдать осторожность), а также вывод таблицы ссылок.
Как автор Web-страницы вы можете создать ее так, чтобы уменьшить трудности,
возникающие при пользовании ею, в частности при выводе на печать. С атрибутом
o n C l i c k гипертекстовой ссылки (содержащей также атрибут TARGET) можно связать
JavaScript-код, который при активизации ссылки передавал бы фокус целевому фрейму.
Дополнительную информацию о JavaScript-сценариях вы найдете в главе 24. Кроме то­
го, для печати пользователю можно предоставить составной документ. Это очень удоб­
но в том случае, когда в состав Web-страницы входят много отдельных документов.

Одновременное обновление нескольких фреймов


в HTML не предусмотрено одновременное обновление нескольких ф р е й м о в
с помощью одной гипертекстовой ссылки. Решить эту задачу можно следующими спо­
собами:
• объединить несколько ф р е й м о в в составе одного элемента FRAME;
• использовать JavaScript.
4.5. Разрешение типичных проблем... 127

Каждое из этих решений подробно описано ниже.

Объединение фреймов в одном элементе FRAME


Предположим, что раздел FRAMESET выглядит следующим образом:
<FRAMESET ROWS="*,*, *">
<FRAME SRC="Top.html">
<FRAME SRC="Middle.html" NAME="MIDDLE">
<FRAME SRC="Bottom.html" NAME="BOTTOM">
</FRAMESET>
В этом случае средствами H T M L нельзя одновременно обновить M i d d l e . h t m l и
B o t t o m , h t m l . Чтобы это стало возможным, надо изменить структуру документа.
<FRAMESET R0WS="*,2*">
<FRAME SRC="Top.html">
<FRAME SRC="Middle+Bottom.html" NAME="LOWER">
</FRAMESET>
Здесь M i d d l e + B o t t o r n , h t m l сам содержит элемент FRAMESET.
<FRAMESET ROWS="*,*">
<FRAME SRC="Middle.html" NAME="MIDDLE">
<FRAME SRC="Bottom.html" NAME="BOTTOM">
</FRAMESET>

Т е п е р ь для ссылок целевым является ф р е й м LOWER, определяющий URL докумен­


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

Использование JavaScript
Данный способ состоит в том, что автор подключает JavaScript-код с помощью ат­
рибута o n C l i c k гипертекстовой ссылки. Фрагмент кода, обрабатывающий событие
o n C l i c k , может содержать любое число команд JavaScript, включая выражение
top .имя_фрейма. location = URL, с помощью которого во ф р е й м е отображается
новый документ. Таким образом мог)Т' обновляться любые ф р е й м ы , при этом не тре­
буется изменять структуру документа верхнего уровня. Очевидно, что подобное ре­
шение возможно только для броузеров, поддерживающих JavaScript. Подробно во­
просы, связанные с JavaScript-кодом, обсуждаются в главе 24, здесь же мы рассмотрим
небольшой пример. В листинге 4.8 приведен HTML-код документа, содержащего один
ф р е й м в верхней строке и три ф р е й м а в нижней строке.
128 Глава 4. Фреймы

Листинг 4.8. Multiple-Updates.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">


<HTML>
<HEAD>
<TITLE>Updating Multiple Frames Simultaneously</TITLE>
</HEAD>
<FRAMESET ROWS="75,*">
<FRAME SRC="Top-Frame.html">
<FRAMESET COLS="*,*^">
<FRAME SRC="Bottoml.html" NAME="Bottoml">
<FRAME SRC="Bottom2.html" NAME="Bottom2">
<FRAME SRC="Bottom3.html" NAME="Bottom3">
</FRAMESET>
</FRAMESET>
</HTML>

Ч т о б ы при активизации ссылки в верхней строке результаты отображались в пер­


вом и третьем ф р е й м а х нижней строки, надо включить в состав документа JavaScript-
программу, которая обращается к целевым фреймам по именам B o t t o m l и B o t t o m S .
Документ, содержащий фрагмент JavaScript-кода, показан в листинге 4.9. В документе
определена функция u p d a t e C e l l s и задана связь этой функции с атрибутом o n C l i c k
гипертекстовой ссылки. На рис. 4.7 и 4.8 показан внешний вид Web-сграницы до и по­
сле активизации ссылки.

Листинг4.9. Top-Frame.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Table of Contents</TITLE>
<SCRIPT TYPE="text/javascript">
<! —
function updateCells() {
top.Bottoml.location = "Resultl.html";
top.Bottoms.location = "Result3.html";
}
// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE" TEXT="BLACK">
When selected on JavaScript-capable browsers,
<A HREF="Resultl.html" TARGET="Bottoml" OnClick="updateCells()">
this link</A> will update cell one and three below.
</BODY>
</HTML>
4.5. Разрешение типичных проблем. 129

швшашшва^ штт
?! 1^ -У 3 rii'^'ai <.!•rfа ш jjj
When selected on JavaScript-с ар able browsers, tins bi^:. will update ceil une and tliree below

Original Original Original


Bottom Bottom Bottom Cell
Celll Cell! 3
mp^' Оосшда* Oone :«- '^ J^:' en -.г'. '

Рис. 4.7. Внешний вид документа до активизации ссылки

StflMrtteneoudly - Hetsdape
fie £(* View £о Qomnurwc^arjd:^^:—^^^^^^^

When selected on JavaScnpt-capable browsers, tins 1шк will update cell one and three below.

Result Cell Original Result Cell


1 Bottom 3
Cell 2
У*«Ф^, ;Docurnenit. Done .>-'„„:ii,A^-.

Рис. 4.8. Внешний вид документа после активизации ссылки

Запрет вывода документа в составе фрейма


Н е к о т о р ы е авторы считают, что их документы не предназначены для отображе­
ния в составе Web-страниц с ф р е й м а м и и стараются принять меры для того, чтобы
запретить подобные действия. Одно из решений данной задачи — включить в раздел
HEAD документа выражение <BASE T A R G E T = " t o p " > . Несмотря на т о что данное вы­
ражение не предотвращает начальный вывод документа, о н о запрещает его появле­
ние в составе документа с ф р е й м а м и после активизации ссылки. Используя JavaScript,
можно полностью запретить о т о б р а ж е н и е документа во ф р е й м е . Для этого в раздел
HEAD надо включить следующий фрагмент кода:
<SCRIPT TYPE="text/javascript">
<! —
if ( t o p . f r a m e s . l e n g t h > 0)
top.location = document.location;
/ / —>
</SCRIPT>
Поскольку некоторые пользователи запрещают выполнение JavaScript-сценариев,
для того, чтобы предотвратить появление документа в составе фрейма, бывает необ­
ходимо использовать оба способа.
130 Глава 4 . Ф р е й м ы

Создание пустого фрейма


Internet Explorer 3.0 и более поздние версии этого броузера поддерживают эле­
менты FRAME, в которых содержится атрибут NAME, но отсутствует атрибут SRC.
В этом случае для такого фрейма резервируется место, определенное посредством
включающего элемента FRAMESET. Если в гипертекстовой ссылке в качестве значения
атрибута TARGET задано имя пустого фрейма, при активизации ссылки в составе этого
фрейма отобразится документ, на который указывает данная ссылка. Резервирование
места для фреймов поддерживается также и в Netscape. Однако гипертекстовые ссыл­
ки, в которых в качестве целевого задан пустой фрейм, ведут себя так, как будто этого
фрейма вовсе не существует, т.е. при активизации ссылки документ выводится в но­
вом окне броузера. Такое несоответствие в поведении броузеров создает определен­
ные неудобства, поскольку автор Web-страницы может посчитать целесообразным
вывести информацию в некоторых фреймах, а другие оставить незаполненными до
тех пор, пока пользователь не выберет соответствующую ссылку. Чтобы созданные
вами Web-страницы не отображались по-разному в разных броузерах, атрибут SRC
лучше рассматривать как обязательный. В этом случае пустой фрейм будет создавать­
ся за счет отображения документа с пустым разделом BODY.
Внимание!

В Netscape при активизации гипертекстовой ссылки, для которой


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

4 . 6 . Встроенные фреймы
в Internet Explorer и Netscape б реализована возможность включать фреймы в со­
став HTML-документа подобно тому, как в документ включаются изображения. Если
вы зададите высоту, ширину и тип выравнивания, встроенный, или плавающий, фрейм
займет определенную позицию в составе Web-страницы. Этим встроенные фреймы
отличаются от обычных фреймов, которые занимают фиксированную позицию в ок­
не броузера. Встроенные фреймы удобно использовать для включения персональных
данных и другой информации, которая должна присутствовать в неизменном виде во
многих документах. Несмотря на то что встроенные фреймы предусмотрены специ­
фикацией HTML 4.0, они реализованы только в Internet Explorer и Netscape 6. Более
ранние версии Netscape не позволяют использовать встроенные фреймы, но поддер­
живают слои (layers), которые дают возможность решать подобные задачи. Чтобы
обеспечить соответствие HTML 4.0, в Netscape 6 была исключена поддержка слоев; об
этом следует помнить, разрабатывая Web-страницы. По адресу h t t p : / / s i t e s .
n e t s c a p e . n e t / e k r o c k / s t a n d a r d s . h t m l вы найдете сведения о том, как использо­
вать встроенные фреймы и слои для работы с различными типами броузеров и для
обеспечения обратной совместимости.
Проиллюстрируем использование встроенных фреймов на следующем примере.
Предположим, некий профессор ведет целый ряд курсов, связанных с компьютерами.
В современных университетах программа курса и прочая информация для студентов
публикуется на Web-страницах. Поэтому профессор должен включать в каждый доку-
4.6. Встроенные фреймы 131

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

Листинг 4.10.Contact-Info,html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">


<HTML>
<HEAD>
<TITLE>Prof. Al Gore Ithim</TITLE>
</HEAD>
<BODY>
Prof. Al Gore Ithim<BR>
Computer Science Departinent<BR>
Podunk University<BR>
<A HREF="mailto:algy@podunk.edu"> algy@podunk.edu</A>
</BODY>
</HTML>

Файл с контактной информацией включается в каждую Web-страницу с помощью


элемента IFRAME. В листинге 4.11 показан упрощенный вариант Web-страницы, по­
священной курсу Computer Science 401.

Листинг 4.11. CS-401.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">


<HTML>
<HEAD>
<TITLE>Design and Analysis of Algorithms</TITLE>
</HEAD>
<BODY>
<Hl>Design and Analysis of Algorithms</Hl>
This course covers the techniques required to design and
analyze computer algorithms. The textbook is <I>Introduction
to Algorithms</I> by Gormen, Leiserson, and Rivest
(McGraw Hill, 1990, ISBN 0-07-013143-0).

<P>
Blah, blah, blah, algorithms. Yada, yada, yada, time. Blah,
blah, blah, space. Yada, yada, yada, iterative. Blah, blah,
blah, recurrences. Yada, yada, yard, data structures. Blah,
blah, blah, sorting. Yada, yada, yada, dynamic programming.
Blah, blah, blah, graph algorithms. Yada, yada, yada,
NP-Completeness.
<P>
<IFRAME SRC="Contact-Info.html" FRAMEBORDER=OX/IFRAME>
</BODY>
</HTML>

Теперь, если отобразить эту Web-страницу в окне Internet Explorer, контактная ин-
форхмация разместится в нижней части Web-страницы, как показано на рис. 4.9 и 4.10.
132 Глава 4. Фреймы

Ш DesMin and Analysis of AlgofHhms - Miaosofl tr^efrtdt EnnAoft»


£ite £ * Yivf* fflvra*e* loot» НФ

Design and Analysis of Algorithms


Ш
This course covers the techniques required to design and analyze computer algorithms The textbook ^^
is Introduction to Algorithms by Cormen, Leiserson, and Rivest (McGraw Hill, 1990. ISBN 0-07- Iv
013143-0). ''
Blah, blah, blah, algorithms. Yada, yada, yada, time. Blah, blah, blah, space Yada, yada, yada,
iterative Blah, blah, blah, recurrences. Yada, yada, yard, data structures. Blah, blah, blah, sorting.
Yada, yada, yada, dynamic programming Blah, blah, blah, graph algorithms. Yada, yada, yada, N P -
Completeness

d
, Щ My CornpUe»
m^^
Рис. 4.9. Верхняя часть документа c s - 4 0 l . h t m l : встроенный фрейм
не отображается

^ Design and Analysis of Algorithms - Microsoft Internet BtfAwm


£ile Edit if«w F^vontes lock НФ

тттшчзу •
Blah, blah, blah, algonthntis. Yada, yada, yada, time. Blah, blah, blah, space. Yada. yada, yada.
Iterative. Blah, blah, blah, recurrences. Yada, yada, yard, data structures Blah, blah, blah, sorting
Yada, yada. yada, dynamic programming Blah, blah, blah, graph algorithms. Yada, yada, yada. I n ­
completeness.

Prof. Al Gore Ithim


Computer Science Department
Podunk University
algy @P 0 dunk, e du

jd
; ^ Done"" i i ^ My Computer

Рис. 4.10. Нижняя часть документа c s - 4 0 1 . h t m l : встроенный фрейм


выводится в окне

Как видите, в отличие от обычных фреймов, встроенные фреймы не занимают


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

Элемент: <IFRAME SRC="..." •..> ... </IFRAME>


А т р и б у т ы : SRC, WIDTH, HEIGHT, ALIGN, NAME, FRAMEBORDER, MARGINWIDTH, MARGINHEIGHT,
SCROLLING, LONGDESC
Элемент I FRAME определяет встроенный, или плавающий, фрейм. Он поддержи­
вается только в Internet Explorer 3.0 и более поздних версиях. Между открывающим и
закрывающим дескрипторами помещается текст, предназначенный для тех броузе­
ров, которые не обрабатывают встроенные фреймы. Если встроенные фреймы под­
держиваются, текст игнорируется.

SRC
Атрибут SRC определяет URL документа, который должен отображаться во встро­
енном фрейме.
4.6. Встроенные фреймы 133

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

ALIGN
Атрибут ALIGN задает тип выравнивания ф р е й м а относительно окружающего тек­
ста. Данный атрибут интерпретируется как и атрибут ALIGN для встроенного изо­
бражения. Допустимыми значениями являются LEFT, RIGHT, CENTER, TOP, BOTTOM
и MIDDLE.

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

FRAMEBORDER
Атриб)п: FRAMEBORDER определяет, должно ли отображаться обрамление вокруг
плавающего фрейма. Значение FRAMEB0RDER=1 указывает на то, что обрамление
должно отображаться, а значение FRAMEBORDER=0 соответственно запрещает вы­
вод обрамления. П о )^1олчанию обрамление выводится.

MARGINWIDTH
Атрибут MARGINWIDTH задает ширину правой и левой границ в пикселях.

MARGINHEIGHT
Атрибут MARGINHEIGHT задает высот)^ верхней и нижней границ в пикселях.

SCROLLING
Атрибут SCROLLING указывает на то, что полоса прокрутки должна выводиться
(SCROLLING="YES"), либо запрещает вывод полосы прокрутки (SCROLLING="NO").

LONGDESC
Атрибут LONGDESC задает URI подробного описания ф р е й м а и в основном исполь­
зуется в невизуальных броузерах.

4.7. Резюме
Фреймы являются важным дополнением к HTML 4.0 и поддерживаются большин­
ством современных броузеров. Элемент FRAMESET делит окно либо текущий ф р е й м
на несколько прямоугольных областей, с которыми связываются HTML-документы.
Для связи документов с областями используется элемент FRAME. Гипертекстовые
ссылки, ф о р м ы и Java-аплеты могут ссылаться на ф р е й м ы , используя имена, опреде­
ленные пользователем, либо стандартные имена. Для того чтобы минимизировать
134 Глава 4. Фреймы

трудности, которые могут возникнуть у пользователей при работе с фреймами, необ­


ходимо тщательно продумывать структуру Web-страницы и при необходимости при­
менять JavaScript-сценарии. Разновидностью фреймов являются встроенные, или
плавающие, фреймы.
Еще одним дополнением к H T M L 4.0 являются каскадные листы стилей. О н и
обеспечивают дополнительный контроль структуры и внешнего вида создаваемых
вами Web-страниц. Расширение каскадных листов стилей позволяет создавать "слои",
посредством которых можно определять разметку абсолютных или относительных
областей страницы. Слои представляют собой более мощное средство, чем ф р е й м ы ,
поскольку определяемые области могут перекрываться, а их размеры и расположение
могут динамически изменяться при выполнении JavaScript-сценариев. Рассмотрению
листов стилей посвящена следующая глава.
КАСКАДНЫЕ
ЛИСТЫ СТИЛЕЙ

В этой главе...

Определение правил стилей.


Связывание правил стилей с HTML-документом.
Способы, позврляющие определить,
поддерживает ли документ правила стилей.
Управление характеристиками шрифтов.
Использование цвета и изображений.
Форматирование текста с использованием стилей.
Управление обрамлением HTML-элементов.
Свойства изображений и плавающих элементов.
Представление списков с помощью листов стилей.
Использование единиц длины и определение цвета.
Организация слоев в документе.
mamJ штат « M J ^ ^^тш^шштш^^ ^mmtammmmi^^ "ттттшшшш^^

аскадные листы стилей— мощное средство форматирования Web-страниц.

К С помощью листов стилей автор может определить начертание и размер шриф­


та, цвет ф о н а и переднего плана, фоновое изображение, размеры границ и дру­
гие характеристики HTML-элементов. Листы стилей не только позволяют управлять
отображением стандартных элементов, но и дают возможность определять новые эле­
менты и накладывать о ф а н и ч е н и я на их характеристики. Правила форматирования
имеют иерархическую, или "каскадную", структуру и позволяют сочетать установки бро­
узера, используемые по умолчанию, с непосредственными инструкциями, задаваемыми
автором док)^ента и пользователем, работающим с Web-страницей. Листы стилей
можно загружать с других узлов, что дает возможность организовывать их совместное
использование. Изменив содержимое одного файла с листами стилей, можно изменять
внешний вид целого набора документов. В настоящее время действует стандарт CSS1
(Cascading Style Sheets, Level 1— Каскадные листы стилей, уровень 1). Листы стилей
поддерживаются версиями 4.01 Netscape и Internet Explorer, а также более поздними
реализациями этих броузеров. Internet Explorer 3.0 частично поддерживает CSS1.
На замену CSS1 World Wide Web Consortium представил CSS2, в котором добавлена
поддержка листов стилей для различных сред. CSS2 позволяет автору создать доку­
мент, предназначенный для отображения в окне обычного броузера, воспроизведе­
ния речи, вывода на п р и н т е р , представления брайлевским ш р и ф т о м и т.д. Кроме то­
го, CSS2 поддерживает позиционирование содержимого, позволяет использовать за­
гружаемые ш р и ф т ы , управлять расположением таблиц, применять национальные
кодировки, автоматическую нумерацию и т.д. В настоящее время ведутся работы по
расширению листов стилей на различные прикладные области. Н и ж е перечислены
документы, в которых содержится и н ф о р м а ц и я о CSS.

Cascading Style S h e e t s , Level 1


http://www.w3.org/TR/REC-CSSl
138 Глава 5. Каскадные листы стилей

Cascading Style S h e e t s , Level 2


http://www.w3.org/TR/REC-CSS2

E x t e n s i b l e Stylesheet L a n g u a g e
http://www.w3.org/Style/XSL/

Новости и предлагаемые расширения листов стилей


http://www.w3.org/Style/
В этой главе будут рассмотрены только средства CSS1.

5 . 1 . Правила стилей
Для управления HTML-элементами используются правила стилей. О б ы ч н о правила
стилей располагаются в отдельном текстовом файле, на который ссылается элемент
LINK, находящийся в разделе HEAD документа. Правила стилей могут также разме­
щаться непосредственно в разделе HEAD HTML-документа и даже в теле Web-
страницы. Правила стилей задаются в следующем виде:
селектор { свойство: значение ]
или
селектор { свойство!: значение!;
СВ0ЙСТВ02: значение2;

СВОЙСТВОМ: значением; }
Если в составе одного селектора определяется несколько свойств, свойства отде­
ляются друг от друга точкой с запятой. За последним свойством точка с запятой мо­
жет не ставиться.
В этой главе будут рассмотрены различные типы селекторов. Самый простой се­
лектор представляет собой имя HTML-элемента и означает, что свойства, указанные в
фигурных скобках, должны применяться ко всем экземплярам этого элемента, при­
сутствующим в составе документа. Свойства и их значения описаны ниже в этой главе.
Рассмотрим простой HTML-документ, код которого представлен в листинге 5.L
Внешний вид документа показан на рис. 5.L

Листинг5.1. F i z z i c s l . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>New Advances in Physics</TITLE>
</HEAD>
<BODY>
<Hl>New&nbsp;Advances&nbsp;in&nbsp;Physics</Hl>

<H2>Turning Gold into Lead</H2>


In a startling breakthrough, scientist B.O. "Gus" Fizzics
has invented a <STRONG>practical</STRONG> technique for
5.1. Правила стилей 139

transmutation! For more details, please see


<A HREF="give-us-your-gold.html">our transmutation thesis</A>.

<H2>Perpetual Inactivity Machine</H2>


In a radical approach that turned traditional attempts to
develop perpetual motion machines on their heads, Prof.
Fizzles has developed a verified, bona-fide perpetual
<STRONG>inaction</STRONG> machine. To purchase your own for
only $99.00 (plus $43.29 shipping and handling), please see
<A HREF="rock.html">our order form</A>.
</BODY>
</HTML>

i j Mew Advances in Physics - Microsoft Internet Expio


f^i'o PdJt View Qo Ffiwoiites Help
ИШ
m ж^- О:
3!.
New Advances in Physics
Turning Gold into Lead
In a startling breakthrough, scientist B.O. "Gus" Fimcs has invented a practical
technique for transmutation! For more details, please see our transmutation thesis.

Perpetual Inactivity Machine


In a radical approach that turned traditional attempts to develop perpetual motion
machines on their heads, Prof Fizzics has developed a verified bona-fide perpetual
inaction machine. To purchase your own for only $99.00 (plus $43.29 shipping and
handling), please see our order form.
HI
pone

Рис. 5.1. Документ F i z z i c s l . h t m l , не исполказующий информацию о стилях

Если в раздел HEAD документа включить приведенный ниже фрагмент кода, доку­
мент будет выглядеть так, как показано на рис. 5.2. Для того чтобы броузеры, не под­
держивающие CSS, не отображали текст между дескрипторами <STYLE> и </STYLE>,
этот текст о ф о р м л е н как комментарии.
<STYLE TYPE="text/css">
<! —
BODY { background: URL(images/confetti-background.jpg) }
HI { text-align: center;
font-family: Blackout }
H2 { font-family: MeppDisplayShadow }
STRONG { text-decoration: underline }
— >
</STYLE>
140 Глава 5. Каскадные листы стилей

Ш New Advances in Physics - Microsoft Internet Explorer


б5б £d'ft yiew iQo Fisp*rt3rites Jjelp
ШШ
О D Й ® a- Ж S- Й
J

"(МгаЛсйз @ < ^ №0® IL^s^


In a startling breakthrough, scientist B.O. "Gus" Fizzics has invented a practical
technique for transmutation! For more details, please see our transmutation thesis.

[p@i^l^tkaal] QiflaeMO^ (N^sx^iiiite


In a radical approach that turned traditional attempts to develop peфetual motion
machines on their heads. Prof Fizzics has developed a verified bona-fide peфetual
inaction machine. To purchase your own for only $99.00 (plus $43.29 shipping and
handling), please see our order fomi.
J
Done

Рис. 5,2. Документ F i z z i c s l . h t m l с использованием информации о стилях

5.2. Внешние и локальные листы стилей


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

Внешние листы стилей


Если вы хотите использовать одинаковые стили для ф о р м а т и р о в а н и я нескольких
документов, можете определить их в отдельном файле. Этот файл надо разместить
так, чтобы WW4V-cepBep имел доступ к нему. Для связывания файла, содержащего
листы стилей, с HTML-документами надо использовать элемент LINK, разместив его в
разделе HEAD. Атрибут REL должен иметь значение STYLESHEET, атрибут HREF опре­
деляет размещение файла, а атрибут TYPE— его тип ( t e x t / e s s ) . Предположим, на­
пример, что внешние листы стилей определены в файле S i t e - s t y l e . e s s , располо­
женном в каталоге e s s . Для того чтобы связать листы стилей с текущим документом,
элемент LINK должен выглядеть следующим образом:
<LINK REL=STYLESHEET
HREF="http://www.oursite.com/css/Site-style.ess"
TYPE="text/css">
Правила стилей определяются для каждого селектора, как это показано в листин­
ге. 5.2. Включать в файл дескрипторы <STYLE> необязательно, поскольку тип данных
определяется посредством атрибута TYPE элемента LINK. Многие авторы используют
дескрипторы <STYLE> и о ф о р м л я ю т правила стилей как HTML-комментарии, однако
в этом нет никакой необходимости.
5 . 2 . В н е ш н и е и локальные листы с т и л е й 141

М е т о д и к а профессионалов

Поддержку HTML'документов на Web-ysne можно упростить, раз­


мещая листы стилей во внешнем файле.

Листинг 5.2.Sitestyle.ess
^9
/* Пример внешнего листа стилей */

HI { text-align: center;
font-family: Arial
}'
H2 { color: #440000;
text-align: center;
font-family: Arial Black, Arial, Helvetica, sans-serif
}

Если правила стилей включаются в раздел HEAD HTML-документа, они помещают­


ся между открывающим и закрывающим дескрипторами <STYLE>.

Элемент: <STYLE TYPE="..." ...> ... </STYLE>


Атрибуты: TYPE (обязательный), MEDIA
Элемент STYLE определяет к о н т е й н е р для правил стилей и может присутствовать
лишь в составе раздела HEAD документа. Элемент STYLE с правилами стилей задается в
следующем формате:
<STYLE TYPE="text/css">
<! —
/* необязательные комментарии */
Правила стилей
-->
</STYLE>
Для того чтобы броузеры, не поддерживающие листы стилей, не отображали
текст между открывающим и закрывающим дескрипторами <STYLE>, правила стилей
оформляются как HTML-комментарии.
М е т о д и к а профессионалов

Оформляйте правила стилей как HTML-комментарии.

TYPE
Атрибут TYPE является обязательным. Он определяет формат, в котором представ­
лены правила. Для каскадных листов стилей задается тип " t e x t / e s s " . Для листов
стилей JavaScript значением атрибута TYPE должно быть " t e x t / j а va s c r i p t " .
142 Глава 5. Каскадные листы стилей

MEDIA
В настоящее время атрибут MEDIA не поддерживается ни Netscape, ни Internet
Explorer. Основное назначение данного атрибута— определять устройство, для
которого должны применяться наборы правил. Предполагается, что в будущем с
документом будут связываться различные наборы правил для различных сред вос­
произведения. Допустимыми значениями атрибута MEDIA являются ALL, AURAL,
BRAILLE, HANDHELD, HELD, PRINT, PROJECTION, SCREEN (значение n o умолча­
нию), SPEECH, TTY и TV.

Элемент STYLE и листы стилей JavaScript


Помимо типа " t e x t / e s s " , Netscape 4.x также поддерживают листы стилей типа
" t e x t / j a v a s c r i p t " . Листы стилей JavaScript используют объекты t a g s и синтаксис
JavaScript. Кроме того, имена свойств представляются несколько по-другому. Например:
<STYLE T Y P E = " t e x t / c s s " >

HI { t e x t - a l i g n : c e n t e r ;
font-family: Arial }
—>
</STYLE>
эквивалентно
<STYLE T Y P E = " t e x t / j a v a s c r i p t " >
<!--
tags.HI.textAlign="center";
tags.HI.fontFamily="Arial";
//—>
</STYLE>
Стандартные листы стилей более универсальны, однако при работе с броузером
Netscape листы стилей JavaScript позволяют не только использовать статические зна­
чения, но и вычислять их. Подробно язык JavaScript рассматривается в главе 24.

Встроенные правила стилей


Спецификация CSS1 позволяет включать и н ф о р м а ц и ю о ф о р м а т и р о в а н и и непо­
средственно в элемент HTML. Для этого используется атрибут STYLE. Встроенные
правила стилей выглядят как обычные правила, но имя элемента и фигурные скобки
не указываются. Например:
<Hl>New Advances in Physics</Hl>
<P STyLE="margin-left: 0.5in;
margin-right: 0.Sin;
font-style: italic">
This paper gives the solution to three
previously unsolved problems: turning lead into gold,
antigravity, and a practical perpetual motion machine.
5.3. Селекторы 143

Встроенные правила переопределяют стили, заданные в разделе HEAD. Стили, за­


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

5.3. Селекторы
Для определения правил стилей в состав элемента STYLE включается элемент,
представленный в следующем виде:
селектор { свойство!: значение!; ...; свойством: значением }
Д о сих п о р мы рассматривали примеры, в которых селекторы представляли собой
имена HTML-элементов и правила применялись ко всем вхождениям указанного эле­
мента в документ. П р и этом стили, связанные с данным элементом, переопределя­
лись. Так, например, чтобы указать, что текст, содержащийся в составе элемента
STRONG, должен отображаться полужирным шрифтом, размер которого на 50% пре­
вышает размер по умолчанию, надо задать следующее выражение:
STRONG { font-weight: bold; font-size: 150% }
Однако типы селекторов не ограничиваются именами HTML-элементов. Каскад­
ные листы стилей также поддерживают селекторы, которые применяются только в
конкретных ситуациях. Н и ж е перечислены четыре наиболее часто применяющихся
типа селекторов:

• HTML-элементы;
• классы, определенные пользователем;
• идентификаторы, определенные пользователем;
• псевдоклассы якоря.
Для указания правил, применяющихся только в определенных условиях, исполь­
зуются атрибуты CLASS, ID и STYLE, которые допустимы для всех элементов за ис­
ключением BASE, BASEFONT (атрибут ID разрешен), HEAD, HTML, МЕТА, PARAM
(атрибут ID разрешен), SCRIPT, STYLE и TITLE. Кроме того, спецификация HTML
4.0 определяет элемент SPAN, с помощью которого стили могут применяться к произ­
вольно выбранным разделам документа.

HTML -элементы
Любой HTML-элемент может быть использован в качестве селектора, однако эле­
мент BR не содержит текст, следовательно, применение к нему правил бессмысленно.
Свойства, установленные с помощью правил, наследуются, например цвет ф о н а для
элемента BODY применяется также и к абзацам в теле документа, а размер шрифта, за­
данный для элемента Р, применяется к содержимому элемента CODE внутри абзаца и т.
д. Передача свойства вложенным элементам продолжается до тех пор, пока это свой­
ство не будет переопределено. Н а п р и м е р , чтобы задать синий цвет для всего текста
за исключением заголовка первого уровня, который должен отображаться красным
цветом, надо указать следующие правила:
144 Глава 5. К а с к а д н ы е листы стилей

BODY { c o l o r : b l u e }
HI { c o l o r : r e d }
Ч т о б ы задать одинаковые стили для нескольких элементов, их надо объединить в
список. Элементы внутри списка разделяются запятыми. Н и ж е приведен п р и м е р ус­
тановки свойств для заголовков различных уровней.
HI, Н2, НЗ, Н4, Н5, Нб { text-align: center;
font-family: sans-serif }
Элемент, содержащийся в составе другого элемента, приобретает свойства вклю­
чающего элемента. Предположим, что для различных элементов стили задаются сле-
д)^ющим образом:
BODY { color: blue }
HI { color: green }
EM { color: red }
В данном примере стиль c o l o r : b l u e , заданный для элемента BODY, наследуется
всеми элементами HTML-документа (Р, UL, 0L, TABLE и т.д.). Поэтому документ будет
отображаться синим цветом за исключением элементов, для которых цвет переопре­
делен, т.е. HI и ЕМ. В данном примере возникает следующая проблема: если элемент
ЕМ прис)а'ствует в составе элемента HI, цвет выделенного текста отличается от цвета
остальной части заголовка. Ч т о б ы это не происходило, необходимо добавить прави­
ло, указывающее на то, что выделенр1ый текст в составе заголовка первого уровня
должен отображаться зеленым цветом.
HI ЕМ { c o l o r : green }
Используя CSS при создании документа, следует помнить, что в броузерах Netscape
4.x имеются н е к о т о р ы е ошибки, связанные с листами стилей. Так, например, иногда
стили некорректно наследуются вложенными контеР1нерами. Чтобы избежать неже­
лательных последствий при отображении документов с помощью этих броузеров, же­
лательно явным образом определять стили для вложенных контейнеров либо приме­
нять классы, определяемые пользователем.

Внимание!

В Netscape 4.x не все стили корректно наследуются дочерними кон­


тейнерами, поэтому тщательно тестируйте свои HTML-документы,
прежде чем отправлять их на Web-yaen.

Классы, определяемые пользователем


Автор документа может определять собствеР1Ные классы селекторов. Имя класса
отделяется от имени НТМЬ-,элемента точкой. Предположим, например, что вам надо
определить некий "абстрактный" тип абзаца с отступами от левой и правой границ, в
котором текст отображается курсивом. Для этого надо указать следующее правило:
Р.abstract { margin-left: 0.5in;
margin-right: 0.5in;
font-style: italic }
5 . 3 . Селекторы 145

Теперь, чтобы использовать класс, надо указать имя класса в качестве значения
атрибута CLASS соответствующего элемента Р. Фрагмент документа, в котором ис­
пользуется "абстрактный" абзац, выглядит следующим образом:
<Hl>New Advances in Physics</Hl>
<P CLASS="abstract">
This paper gives the solution to three previously unsolved
problems: turning lead into gold, antigravity, and a
practical perpetual motion machine.
CSS позволяет определять классы, применимые ко всем HTML-элементам. Такой
класс надо объявлять без указания имени элемента перед именем класса. Например,
следующее выражение определяет класс, в котором задается синий цвет переднего
плана и полужирный шрифт:
.blue { color: blue; font-weight: bold }
Этот стиль может быть использован в уже существующем абзаце
This text is in the default color, but
<SPAN CLASS="blue">this text is blue.</SPAN>
либо применен к целому блоку.
<Н2 CLASS="blue">A Blue Heading</H2>
Заметьте, что в именах классов, определенных пользователем, Netscape не под­
держивает символ подчеркивания. Так, например, Netscape не будет интерпретиро­
вать . b l u e _ f o n t как стиль.
Внимание!

Netscape не распознает имена классов, содержащих символ под­


черкивания ("_").

Идентификаторы, определяемые пользователем


И д е н т и ф и к а т о р ы похожи на классы, но, в отличие от класса, и д е н т и ф и к а т о р мо­
жет применяться в составе документа лишь единожды. Для того чтобы объявить
идентификатор, надо указать перед именем символ "#". Элемент ссылается на опре­
деление идентификатора посредством атрибута ID.
<HEAD>
<TITLE>...</TITLE>
<STYLE TYPE="text/css">
<! —
#foo { color: red }
— >
</STYLE>
</HEAD>
<BODY>

<P ID="foo">

</BODY>
В большинстве случаев классы предпочтительнее идентификаторов.
146 Глава 5. Каскадные листы стилей

Псевдоклассы якоря
Поскольку в языке HTML для определения гипертекстовой ссылки предусмотрен
единственный элемент (элемент А, который п р и н я т о называть якорем), он использу­
ется независимо от того, была ли ссылка просмотрена ранее или выбрана в настоя­
щий момент. Стандарт CSS1 позволяет определять свойства отдельно для каждого ти­
па ссылки. Для указания типа ссылки используются следующие селекторы.

A:link, и л и :link
Этот селектор определяет ссылку только в том случае, если она не была просмот­
рена раньше. Броузер определяет, посещалась ли ссылка раньше, на основании
списка предыстории.

A:visited, и л и rvisited
Д а н н ы й селектор соответствует только тем ссылкам, которые не посещались
раньше.

А:active, и л и :active
Этот селектор определяет, как должна отображаться выбранная ссылка (ссылка
считается выбранной до отпускания кнопки мыши).

Aihover, и л и :hover
Этот селектор поддерживается только Internet Explorer. Он применяется к ссылке,
на которую указывает курсор мыши.
Псевдоклассы могут объединяться с другими селекторами. Н а п р и м е р , следующее
правило применяется только к выбранной ссылке и только в том случае, если она
входит в состав элемента указанного класса:
.bizarre lactive { font-size: 300% }
Приведенное ниже правило применимо к изображению, оформленному в виде ги­
пертекстовой ссылки. Правило применяется до тех пор, пока ссылка не будет активи­
зирована.
А:link IMG { border: solid green }

5.4. Предшествование правил


Для конкретного фрагмента текста могут быть определены различные правила
стилей, поэтому броузер должен знать последовательность применения этих правил.
Правила с наивысшим приоритетом, т.е. правила, которым соответствует наиболь­
шее значение предшествования, применяются в последнюю очередь и замещают зна­
чения, заданные правилами с более низким приоритетом. Порядок определения
предшествования (каскадирование) описан ниже.
1. Правила, помеченные как "important" (валсные), имеют наивысший
приоритет.
5.5. Свойства шрифтов 147

Правило стиля может включать модификатор ! i m p o r t a n t . Например, в сле­


дующем примере этим модификатором помечено свойство, которое задает
цвет переднего плана:
HI { c o l o r : b l a c k ! i m p o r t a n t ;
font-family: sans-serif }
Модификатор ! i m p o r t a n t используется крайне редко.
2. Правила, определенные автором, предпочтительнее правил, определен­
ных пользователем, просматривающим документ.
Броузеры позволяют пользователям, просматривающим документ, создавать
стили, заменяющие установки по умолчанию. В этом случае установки, выпол­
ненные автором Web-страницы, более приоритетны, чем установки, выпол­
ненные пользователем.
3. Конкретные правила приоритетнее более общих правил.
При сравнении правил наибольший приоритет имеют идентификаторы. В ос­
тавшейся группе разделение осуществляется по числу атрибутов класса в селек­
торе. В последнюю очередь учитывается число HTML-элементов, указанных в
селекторе. Приведенные ниже правила рассортированы так, что более кон­
кретные правила расположены в начале, а более общие правила— в конце.
В первом правиле селектором является идентификатор. Во втором правиле
указан класс (big), поэтому оно приоритетнее третьего и четвертого правил.
И, наконец, третье правило более конкретно по сравнению с четвертым пра­
вилом, так как в нем присутствуют два HTML-элемента.
#foo { . . . }
р . b i g HI { . . . }
Р STRONG { .. . }
STRONG { . . . }

4. Если нет оснований предпочесть одно правило другому, правило, определен­


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

5.5. Свойства шрифтов


CSS1 позволяет автору управлять различными характеристиками шрифтов, зада­
вать отображение полужирным шрифтом или курсивом, определять размер и начер­
тание шрифта и т.д. Броузер Netscape также поддерживает динамические шрифты,
позволяющие связать файл шрифтов с документом; при этом документ перестает за­
висеть от набора шрифтов, установленных в клиентской системе. Текст с подчерки­
ванием, верхние и нижние индексы в данном разделе не рассматриваются; они опре­
деляются свойствами текста t e x t - d e c o r a t i o n и v e r t i c a l - a l i g n , которые будут
рассмотрены далее в этой главе. При рассмотрении стилей значения непосредствен­
но следуют за именами стилей. Значение, принимаемое по умолчанию, выделяется
полужирным шрифтом.
148 Глава 5. Каскадные листы стилей

font-weight
normal I lighter | bold | bolder 110012001... 1900
Этот стиль определяет вес шрифта и задается числовыми значениями от 100
(минимальный) до 900 (максимальный) с шагом 100. Кроме того, могут быть исполь­
зованы символьные обозначения: normal, l i g h t e r , bold и b o l d e r . Например:
HI { font-weight : 200 }
Н2 { font-weight : bolder }
Netscape не поддерживает значение b o l d e r .

font-style
normal I italic | oblique
Это свойство выбирает тип шрифта в составе конкретного семейства шрифтов.
Допустимыми значениями являются normal, i t a l i c и o b l i q u e .
Р { font-style : normal }
ТН { f o n t - s y t l e : italic }
Netscape не поддерживает значение o b l i q u e .

font-size
pt, pc, in, cm, mm | em, ex, px, % |
xx-large I x-large | large | medium | small | x-small | xx-small |
smaller | larger
Данный стиль определяет размер шрифта. Значение задается в стандартных еди­
ницах длины (см. табл. 5.1), с помощью символьных имен либо в процентах. Сим­
вольные значения могут быть абсолютными ( x x - l a r g e , x - l a r g e , l a r g e , medium,
s m a l l , x - s m a l l , x x - s m a l l ) или относительными ( s m a l l e r или l a r g e r ) . Значе­
ние в процентах вычисляется относительно размера шрифта в контейнере. На­
пример, следующее выражение означает, что текст в составе элемента STRONG
должен быть на 50% больше текущего размера шрифта:
STRONG { font-size: 150% }
Ниже приведены примеры использования свойства f o n t - s i z e .
Р { f o n t - s i z e : 14pt }
Р { f o n t - s i z e : 1cm }
Р { font-size: xx-large }
Для того чтобы броузер Netscape распознавал стили, необходимо между числовой
величиной и обозначением единиц измерения вставлять пробел, т.е. вместо
" 14 р t" надо задавать "14 р t".

font-family
имя семейства шрифтов
Данное свойство определяет начертание шрифтов. Например, в листинге 5.3 при­
веден пример простой Web-страницы; ее внешний вид показан на рис. 5.3.
5.5. Свойства шрифтов 149

Листинг 5.3. Web-страница Camp Bear Claw

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Cainp Bear Claw</TITLE>
</HEAD>
<BODY>
<Hl>Camp Bear Claw</Hl>
We have the following activities:
<H2>Archery</H2>
<H2>Arts and Crafts</H2>
<H2>Horseback Riding</H2>
<H2>Hiking</H2>
<H2>Campfire Song Times</H2>
<H2>C++ Prograiraning</H2>
</BODY>
</HTML>

О Camp Bear Claw - Microsoft internet Expio


Die Edit yiew Qp Favorites tjetp
mm
Ф о о Ш Q a- (В м^
"U
Camp Bear Claw
We have the following activities:

Archery
Arts and Crafts
Horseback Riding
Hiking
Campfire Song Times
C++ Programming
Рис. 5.3. Web-страница Bear Claw со
стандартными шрифтами

Добавляя свойства f o n t - f a m i l y (см. листинги 5.4 и 5.5), документ можно отобра­


зить совсем по-другому.

Листинг 5.4. Модифицированный код Web-страницы Camp Bear Claw

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
150 Глава 5. Каскадные листы стилей

<TITLE>Camp Bear Claw</TITLE>


<LINK REL^STYLESHEET HREF="CampBearClaw.ess" TYPE="text/ess">
</HEAD>
<BODY>
<Hl>Camp Bear Claw</Hl>
We have the following activities:
<H2 CLASS="archery">Archery</H2>
<H2 CLASS="arts">Arts and Crafts</H2>
<H2 CLASS="horseback">Horseback Riding</H2>
<H2 CLASS="hiking">Hiking</H2>
<H2 CLASS="campfire">Campfire Song Times</H2>
<H2 CLASS="java">Java Programming</H2>
</BODY>
</HTML>

Листинг 5 . 5 . C a m p B e a r C l a w . c s s

HI { text-align: center;
font-family: Funstuff }
H2.archery { font-family: ArcheryDisplay }
H2.arts { font-family: ClampettsDisplay }
H2.horseback { font-family: Rodeo }
H2.hiking { font-family: SnowtopCaps }
H2.campfire { font-family: Music Hall }
H2.java { font-family: Digiface }

i j Camp Bear Clew - Microsoft Internet Expio


File £dft View Qo fsyohtes tlelp

. о • QD a (й m- 3 M ч1

C^Xhf Bear CUw
We have the following activities:

^Ь aoodi £raf fts

HORSEBACK: RIDIHC
И1К11Ю

JRVR PR05RRmmin5
Dom
Ш: Рис, 5.4. Web-страница Bear Claw со

^ш. шрифтами, определенными автором


5.5. Свойства шрифтов 151

Заметьте, что если задать свойство f o n t - f a m i l y в качестве значения атрибута


STYLE и поместить его в двойные кавычки, то имя шрифта, состоящее из нескольких
слов, должно быть указано в одинарных кавычках. Кроме того, имена шрифтов могут
быть заданы в виде списка, в котором имена разделяются запятыми. Порядок следова­
ния имен в списке отражает предпочтения автора. По возможности броузер выберет
первый шрифт, указанный в списке. Наиболее часто используемые шрифты, например
"sanserif, можно указать в конце списка. Если шрифты, помещенные в начало списка,
окажутся недоступными, такой шрифт будет играть роль значения по умолчанию.
Правильно выбирая шрифты, можно существенно улучшить внешний вид Web-
страниц, особенно в intranet, где на всех компьютерах сети установлен стандартный
набор шрифтов. Создавгш документы для Web, вряд ли возможно выбрать шрифт, ко­
торый был бы установлен на всех клиентских системах. Если вы будете применять
псевдографику для отобрг1жения небольших пиктограмм, то при отсутствии такого
шрифта внешний вид страницы станет неприемлемым. В подобной ситуации лучше
использовать для этих целей GIF-файлы или изображения в другом формате, однако
следует помнить, что время загрузки Web-страницы увеличится. Кроме того, символы
национальных кодировок, представленные как изображения, будут недоступны поис­
ковым роботам, выполняющим индексацию содержимого Web. В CSS2 предложено
полезное расширение — динамические шрифты. Далее в этом разделе будут описаны
свойства f o n t d e f и f o n t - f a c e для управления динамическими шрифтами соответ­
ственно в Netscape и Internet Explorer.

font-variant
normal I small-caps
Данное свойство применяется для отображения текста малыми прописными бук­
вами. Допустимыми значениями являются normal и s m a l l - c a p s . Internet
Explorer отображает s m a l l - c a p s обычными прописными буквами. Netscape не
поддерживает это свойство.

font
Свойство f o n t позволяет объединять свойства f o n t - w e i g h t , f o n t - v a r i a n t ,
f o n t - s t y l e , f o n t - s i z e , l i n e - h e i g h t и f o n t - f a m i l y в одной записи. Компо­
ненты значения могут не указываться, но если они присутствуют, то должны сле­
довать в определенном порядке и разделяться пробелами. Исключение составляют
значения f o n t - s i z e и l i n e - h e i g h t , которые разделяются косой чертой ("/")•
Например, код
Р { f o n t - w e i g h t : demi-bold;
font-style: i t a l i c ;
f o n t - s i z e : 14pt;
l i n e - h e i g h t : 150%;
f o n t - f a m i l y : Times, s e r i f }
можно заменить выражением
P { f o n t : demi-bold i t a l i c 14pt/150% Times, s e r i f }
152 Глава 5. Каскадные листы стилей

fontdef
Поддержка динамических ш р и ф т о в реализована как расширение CSSI. П р и этом с
HTML-документом связывается файл ш р и ф т о в . Для работы с динамическими
шрифтами в Netscape может быть использован как стиль f o n t d e f ,
<STYLE TYPE="text/css">
<! —
@fontdef URL(http://.../font-file.pfr);

-->
</STYLE>
так и элемент LINK, расположенный в разделе HEAD документа.
<LINK REL="fontdef"
SRC="http://.../font-file.pfr">
После связывания документа с файлом . p f r динамически загружаемые ш р и ф т ы
можно использовать как обычные ш р и ф т ы . Internet Explorer не поддерживает
ф о р м а т p r f , однако компания Bitstream выпустила управляющий компонент
ActiveX, позволяющий Internet Explorer обрабатывать файлы . p f r . Дополнитель­
ная и н ф о р м а ц и я о динамических ш р и ф т а х находится по следующим адресам:
http://www.truedoc.com/webpages/intro/
http://www.bitstream.com/
Там же вы найдете свободно распространяемые ф а й л ы шрифтов.

font-face
Для поддержки динамически загружаемых ш р и ф т о в Internet Explorer использует
свойство f o n t - f a c e , определенное в Style Sheets, Level 2 ( h t t p : / / w w w . w 3 . o r g /
T R / R E C - C S S 2 / f o n t s . h t m l # f o n t - d e s c r i p t i o n s ) . Для того чтобы задать дина­
мический ш р и ф т для Internet Explorer, используется код наподобие следующего:
<STYLE TYPE="text/css">
<! —
@font-face {
font-family: fontname;
font-style: normal;
font-weight: normal;
src: url(http://.../font-file.eot)
}
</STYLE>
Заметьте, что Internet Explorer и Netscape работают с различными форматами
файлов ш р и ф т о в . Internet Explorer использует ф о р м а т e o t , а Netscape — p f r . Для
создания файлов . e o t Microsoft предоставляет инструмент Web E m b e d d i n g Fonts
Tool (WEFT). П р и м е р ы динамических ш р и ф т о в Microsoft находятся по адресу
http://www.microsoft.com/typography/
С этого же узла можно скопировать свободно распространяемую программу WEFT
Version 2.
5.6. Свойства для определения фона и переднего плана 153

5.6. Свойства для определения фона


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

color
имя_цвета \ #RRGGBB | #RGB | rgb(rrr, ggg, bbb) | rgb(rrr%, ggg%. bbb%)
Данное свойство определяет цвет текста или цвет переднего плана для раздела доку­
мента. Для определения цвета используются стандартные значения (см. табл. 5.2).
Ниже приведены примеры использования свойства c o l o r .
р { color blue }
HI { color #OOAABB }
Н2 { color #OAB }
НЗ { color rgb(255, 0, 0 ) } /* red -^f
Н4 { color rgb(0, 0, 255 ) } /^ blue */

background-color
transparent |
имя_цвета \ #RRGGBB | #RGB | rgb(rrr, ggg, bbb) | rgb(nT%, ggg%, b b b % )
Данное свойство задает цвет ф о н а для раздела документа. Для определения цвета
используются стандартные значения, кроме того, чтобы задать отображение пер­
воначального цвета сквозь п р о з р а ч н ы й фон, можно использовать ключевое слово
transparent.

background-image
n o n e I иг\{имя_файла)
Данное свойство определяет изображение, которое может быть использовано в ка­
честве фона для раздела документа. На тот случай, если файл с изображением недос­
тупен либо если в броузере запрещена загрузка изображений, автор документа может
также задать цвет фона. Следующее выражение задает фоновое изображение:
Н2 { background-image: url(Bluedrop.gif);}

background-repeat
repeat | repeat-x | repeat-y | norepeat
Данное свойство может приобретать значения r e p e a t , r e p e a t - x , r e p e a t - y или
n o r e p e a t и соответственно означает, что вывод изображения повторяется в двух
направлениях, только по оси х, только по оси у, либо отображается один раз и не
может повторяться. Ниже приведен пример использования свойства b a c k g r o u n d -
repeat.
154 Глава 5. Каскадные листы стилей

BODY {
background-image: url(Bluedot.gif);
background-repeat: repeat-x;

background-attachment
scroll I fixed
Данное свойство определяет, должно ли фоновое изображение прокручиваться
вместе с содержимым документа (значение s c r o l l ) либо оставаться неподвиж­
ным (значение f i x e d ) . Данное свойство не поддерживается Netscape.

background-position
[ top I center | bottom] [ left | center | right ] |
[ pt, pc, in, cm, mm ] [ pt, pc, in, cm, mm ] |
[ em, ex, px, % ] [ em, ex, px, % ]
Свойство b a c k g r o u n d - p o s i t i o n задает позицию фонового изображения отно­
сительно верхнего левого края области. Для данного свойства обычно задаются
два значения, разделенные пробелами. Указанные значения уточняются посредст­
вом ключевых слов l e f t / c e n t e r / r i g h t , t o p / m i d d l e / b o t t o m , знака процента
и обозначения единицы длины (см. табл. 5.1). Так, например, 50% означает, что
центр изображения должен располагаться в центре области. Значение 25рх по го­
ризонтали указывает на то, что левая граница изображения должна отступать на
25 пикселей от левой границы области. Если вместо двух значений задано одно,
считается, что указана позиция по горизонтали, а позиция по вертикали принима­
ется равной 50%. По умолчанию принимается значение "0% 0%". Отрицательные
значения также поддерживаются, в этом случае считается, что изображение вы­
ступает за границы раздела. Ниже приведены примеры использования свойства
background-position.
BODY { background-image: url(Marty.jpg);
background-position: 10% 10%; }
HI { background-image: Bluedrop.gif;
background-position: center; } /* 50% 50% */

Броузер Netscape не поддерживает свойство b a c k g r o u n d - p o s i t i o n .

background
Свойство background позволяет объединять свойства b a c k g r o u n d - c o l o r , b a c k ­
ground-image, b a c k g r o u n d - r e p e a t , b a c k g r o u n d - a t t a c h m e n t и b a c k g r o u n d -
p o s i t i o n в одной записи.
В качестве примера рассмотрим листинги 5.6 и 5.7, которые содержат код Web-
страницы. Заголовок отображается на фоне, имитирующем деревянные панели.
Формат изображения повторяется по горизонтали. Внешний вид Web-страницы в ок­
не броузера Netscape Communicator 4.7 показан на рис. 5.5.
5.6. Свойства для определения фона и переднего плана 155

Листинг 5.6. Cabinets. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Joe's Cabinets</TITLE>
<LINK REL=STYLESHEET HREF="Cabinets.ess" TYPE="text/css">
</HEAD>
<BODY>
<CENTER>
<TABLE WIDTH=360 HEIGHT=199>
<TR><TD ALIGN="CENTER" CLASS="banner">Joe's Cabinets
</TABLE>
</CENTER>
<P>
Welcome to Joe's Cabinets. We specialize in
<UL>
<LI>Custom Cabinets
<LI>Kitchen Remodeling
<!-- Etc -->
</UL>
< ! — Etc — >
</BODY>
</HTML>

Листинг 5.7. Cabinets.ess

.banner { background: url(images/boards.jpg) repeat-x;


font-size: 50pt;
font-family: Arial Rounded MT Bold }

ШЕВШШВШШ

ltki>'M^mail.,.nM.
Welcome to Joe's Cabinets. We specialize in

• Custom Cabinets ,
• Kitchen Remodebng

Рис. 5.5. Фоновое изображение может применять­


ся при выводе отдельных блоков текста
156 Глава 5. Каскадные листы стилей

5.7. Текстовые свойства


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

word-spacing, letter-spacing
normal I + / - pt, pc, in, cm, m m | + / - em, ex, px
Данные свойства изменяют расстояния между словами и между символами, приня­
тые по умолчанию. Для указания расстояния используются стандартные единицы
длины (см. табл. 5.1) либо ключевое слово n o r m a l . Числовые значения мог)т быть
либо положительными (указанная величина добавляется к стандартному расстоя­
нию), либо отрицательными (заданная величина вычитается из стандартного рас­
стояния). Свойство w o r d - s p a c i n g не поддерживается ни Netscape, ни Internet
Explorer. Кроме того, Netscape не поддерживает свойство l e t t e r - s p a c i n g .

text-decoration
n o n e I underline | overline | line-through | blink
Свойство t e x t - d e c o r a t i o n описывает дополнительные элементы текста. Допус­
тимыми значениями являются п о п е , u n d e r l i n e , o v e r l i n e , l i n e - t h r o u g h и
b l i n k . Например, если вы хотите, чтобы гипертекстовые ссылки отображались
синим цветом, но без подчеркивания, а текст абзаца выводился с подчеркиванием,
надо задать следующие выражения:
А:link { coloriblue; text-decoration: none }
P { text-decoration: underline }
Заметьте, что Internet Explorer не поддерживает значение b l i n k , a Netscape не
поддерживает значение o v e r l i n e .

vertical-align
top I bottom I baseline | middle | sub | super | text-top | text-bottom | %
Данное свойство определяет позиционирование элементов по вертикали. Значение,
заданное в процентах (положительное или отрицательное), определяет смещение
базовой линии элемента от базовой линии родительского элемента. Кроме того,
значение может задаваться посредством символьных имен. Допустимыми символь­
ными именами являются t o p (выравнивание верхней части данного элемента по
верхней части самого высокого элемента строки), b o t t o m (выравнивание нижней
части данного элемента по нижней части самого низкого элемента строки), b a s e ­
l i n e (выравнивание базовой линии данного элемента по базовой линии родитель­
ского элемента), m i d d l e (выравнивание середины элемента по половине расстоя­
ния над базовой линией родительского элемента), s u b (представление элемента как
нижнего индекса), s u p e r (представление элемента как верхнего индекса), t e x t - t o p
(выравнивание верхней границы по верху шрифта родительского элемента), t e x t -
b o t t o m (выравнивание нижней границы по низу шрифта родительского элемента).
5-7- Текстовые свойства 157

text-transform
n o n e I uppercase | lowercase | capitalize
Данное свойство определяет, должно ли выполняться преобразование текста в сим­
волы верхнего регистра ( u p p e r c a s e ) , в символы нижнего регистра ( l o w e r c a s e ) и
следует ли начинать каждое слово с прописной буквы ( c a p i t a l i z e ) .

text-align
left I right I center [justify
Данное свойство задает выравнивание абзаца по левой границе, по центру, по
правой границе и по обеим границам.

text-indent
+ / - pt, рс, in, cm, mm | + / ~ em, ex, px, %
Данное свойство задает отступ первой строки абзаца. Отступ отсчитывается от ле­
вой границы, определенной посредством свойства m a r g i n - l e f t . Значения зада­
ются в стандартных единицах длины либо вычисляются в процентах от ш и р и н ы
родительского элемента. По умолчанию принимается значение 0. Отрицательная
величина означает, что первая строка выступает за левую границу. Например:
Р { text-indent: -25рх } /* Выступ */

line-height
normal I число \ pt, рс, in, cm, mm | em, ex, px, %
Данное свойство задает высоту строки — расстояние между базовыми линиями
двух последовательно расположенных строк абзаца. Значение может быть задано в
стандартных единицах длины, а также в процентах от размера шрифта. Например:
.double { line-height: 200% }
.triple { line-height: 3 } /* Зх the font size */
DIV { line-height: 1.5em }

white-space
normal I pre | nowrap
Свойство w h i t e - s p a c e определяет особенности интерпретации пробелов, сим­
волов табуляции, возврата каретки и новой строки в пределах элемента. Допусти­
мыми значениями являются n o r m a l (преобразование нескольких последователь­
но расположенных пробелов в один пробел), p r e (пробелы интерпретируется так
же, как в элементе PRE) и n o w r a p (перевод строки осуществляется только посред­
ством элемента BR). Броузер Netscape не распознает значение n o w r a p ; Internet
Explorer вовсе не поддерживает свойство w h i t e - s p a c e .
В качестве примера рассмотрим Web-страницу, которая должна выглядеть как де­
ловое письмо. Исходный код этой Web-страницы представлен в листинге 5.8. Рас­
стояние между абзацами уменьшено с помощью следующего выражения:
Р { margin-top: 5рх }
Для даты, адреса получателя и отправителя определены классы абзацев, выров­
ненные по правой ( r h e a d ) и левой ( I h e a d ) границам. Основное содержание доку-
158 Глава 5. Каскадные листы стилей

мента выводится с отступом и выравниванием ( j u s t i f y ) . Кроме того, подпись


( f o o t ) отображается с отступом 60% и увеличенным расстоянием между строками.
На рис. 5.6 показан внешний вид документа, отображаемого в броузере Internet
Explorer, который выполняется в среде Windows 2000.

Листинг 5 . 8 . B a t e s . html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>An Open L e t t e r t o t h e IRS</TITLE>
<LINK REL=STYLESHEET H R E F = " B a t e s . e s s " T Y P E = " t e x t / c s s " >
</HEAD>
<BODY BACKGROUND="images/bond-paper.jpg">
<P CLASS="rhead">
April 1, 2001
<HR>
<P CLASS="rhead">
William A. Bates<BR>
Macrosoft Corporation<BR>
Blumond, WA 12345
<P CLASS="lhead">
Internal Revenue Service<BR>
Philadelphia, PA 67890
<P>
<BR>
Dear Sirs,
<P CLASS="body">
I am writing to inform you that, due to financial difficulties,
I will be unable to pay my taxes this year.
<P CLASS="body">
You see, my company has had reduced profits this year. In fact
gross revenues have now dropped below the GDP of <B>twelve</B>
foreign countries! Given this intolerable situation, I am sure
you will understand.
<P CLASS="foot">
Sincerely,<BR>
William A. Bates
</BODY>
</HTML>

Листинг 5.9. Bates. ess

P { margin-top: 5px }
P.rhead { text-align: right;
margin-right: O.Sin;
font-family: sans-serif }
P.lhead { font-family: sans-serif }
P.body { text-align: justify;
text-indent: 0.5in }
P.foot { margin-left: 60%;
line-height: 300% }
5.8. Свойства блоков с обрамлением 159

Ш An Open Letter to the IRS - MkrosofI: MefneiC«ok»^ ^ ^iptxl


Fte Edit View Favorites loot Help

*« - •* - '^ i3 ^ '3^Sedfch ; i j Favorites ^History -^- ^ @ •

April 1,2001
"3

VViiliam A. Bates
Macrosoft Софогайоп
Blumood.WA 12345 f
Internal Revenue Service
Philadelphia, PA 67890

Dear Sirs,
I am writing to inform you that, due to financial difficulties, I wiD be unable to
pay ray taxes this year.
You see, my company has had reduced profits this year. In fact gross
revenues have now dropped bdow the GDP of twelve foreign countries! Given this
intolerable situation, I am sure you will understand

Sincerely,

Williain A. Bates

zl
i^Oone ( ^ My Computer

Рис. 5.6. Текстовые свойства позволяют форматировать документ тре­


буемым образом

5.8. Свойства блоков с обрамлением


При использовании каскадных листов стилей предполагается, что все элементы
отображаются в одной или нескольких прямоугольных областях. Такие области назы­
ваются блоками (box) и содержат обрамление, заполненное пространство и включен­
ные элементы. Ширина и высота блока определяются шириной и высотой включен­
ных элементов, размерами заполненного пространства, окружающего элементы, раз­
мерами обрамлейия вокруг блока и границами вокруг обрамления. Границы вокруг
обрамления всегда прозрачны и не закрывают фон. Пространство вокруг элемента
всегда заполняется цветом фона либо фрагментами фонового изображения, опреде­
ленного для этого элемента. Для обрамления может быть задан отдельный фон. Так,
например, чтобы задать размеры границ, обрамления и заполненной области, рав­
ными четверти дюйма, надо использовать следующий фрагмент кода. При отображе­
нии указанных компонентов будет выводиться разный фон.
Р { margin: 0 . 2 5 i n ;
border; 0.25in solid black;
padding: 0.25in;
background: URL(images/bond-paper.jpg) }
BODY { background: URL(images/bricks.jpg) }
Ha рис. 5.7 показана Web-страница, созданная с применением заданных свойств.
Заметьте, что границы отображаются за пределами обрамления, обрамление — за
пределами заполненной области, а заполненная область — за пределами элемента.
160 Глава 5. Каскадные листы стилей

ЩBaundift9 Box - MftcrosoftliAetiii^Шщ^ШтМ

Jcjk £c8t ^ew Fuvorltes look «elp

I ш1 ^
^Щ^^^^^Лм!

-^Done ^ ^ My Computer

Рис. 5.7. Отображение границ, обрамления и заполнен­


ной области

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

margin-left, margin-right, margin-top, margin-bottom


auto I + / - pt, pc, in, cm, m m | + / - em, ex, px, %
Данные свойства задают левую, правую, верхнюю и н и ж н ю ю границы. Для указа­
ния размеров границ используются стандартные единицы длины (см. табл. 5.1),
относительные значения в процентах или ключевое слово a u t o . По умолчанию
принимается значение 0. Отрицательные значения также допустимы, они опреде­
ляют выступ за левую границу либо перекрытие предыдущего абзаца. П р и м е р ы
определения границ приведены ниже.
Р { m a r g i n - r i g h t : 5ех }
HI { m a r g i n - t o p : 200% }
margin
Данное свойство позволяет задавать верхнюю, правую, нижнюю и л€;вую границы
(в указанном порядке) с помощью одной записи. Если указано только одно значе­
ние, оно применяется для определения всех четырех границ. Если заданы два или
три значения, недостающая величина принимается равной размеру противопо­
ложной границы. Отрицательные значения допустимы, их иногда используют для
создания эффекта расположения текста "в несколько слоев".

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

border-left-width, border-right-width, border-top-width, border-bottom-width


none I thin | medium | thick |
pt, pc, in, cm, mm | em, ex, px
Данные свойства задают размеры левой, правой, верхней и нижней частей обрам­
ления. Для указания размеров используются стандартные единицы длины либо
символьные имена t h i n , medium, t h i c k или none. Отрицательные значения за­
прещены.

border-width
none I thin | medium | thick |
pt, pc, in, cm, mm | em, ex, px
Данное свойство позволяет в одной записи задавать величины b o r d e r - w i d t h -
top, b o r d e r - w i d t h - r i g h t , border-width-bottom и b o r d e r - w i d t h - l e f t
(значения задаются в указанной последовательности). Если задано только одно
значение, оно применяется для определения всех четырех частей обрамления. Ес­
ли заданы два или три значения, недостающая величина принимается равной раз­
меру противоположной части. Например, в приведенном ниже выражении для
верхней и нижней частей обрамления принимается значение medium, а для пра­
вой и левой частей — значение t h i n .
DIV { b o r d e r - w i d t h : medium t h i n }

border-color
имя_цвета \ #RRGGBB | #RGB | rgb(rrr, ggg, bbb) | rgb(rrr%, ggg%, bbb%)
Данное свойство задает цвета различных частей обрамления. Подобно b o r d e r -
width, для данного свойства указываются четыре значения, которые определяют
характеристики соответственно верхней, правой, нижней и левой частей обрам­
ления. Способы определения значений цветов описаны в табл. 5.2. Например:
Р { border-style: solid;
b o r d e r - c o l o r : b l a c k gray gray b l a c k ;
}

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


Netscape может обрабатывать его некорректно.

border-style
none I dotted | dashed | solid | double | groove | ridge | inset | outset
Данное свойство задает способ отображения обрамления. Для него могут быть зада­
ны от одного до четырех значений, определяющих, подобно b o r d e r - w i d t h , харак­
теристики верхней, правой, нижней и левой частей обрамления. Допустимыми яв­
ляются значения попе, d o t t e d , dashed, s o l i d , double, groove, r i d g e , i n s e t и
o u t s e t . В областях обрамления, не заполненных цветом переднего плана, отобра­
жается цвет фона или фоновое изображение включаемого элемента. Значения
dashed и d o t t e d не поддерживаются ни Netscape, ни Internet Explorer. Следует за­
метить, что Netscape не поддерживает свойства b o r d e r - s t y l e ; соответствующие
значения должны задаваться посредством свойства border. Например,
162 Глава 5. Каскадные листы стилей

Р { border-sytle: ridge }
не распознается Netscape; вместо этого следует задать следующую конструкцию:
Р { border: ridge }
Internet Explorer корректно обрабатывает оба выражения.

border-left, border-right, border-top, border-bottom


Данные свойства позволяют задавать размеры, стиль и цвет для каждой из четы­
рех частей обрамления. Так, например, чтобы заголовки первого уровня выводи­
лись красным цветом, а сверху и снизу заголовка отображалась синяя сплошная
линия, надо задать следующее выражение:
HI { c o l o r : red;
border-top: lOpx solid blue;
border-bottom: lOpx solid blue }

border
Данное свойство определяет размеры, стиль и цвет для всех четырех частей об­
рамления. Например, сплошное обрамление черного цвета шириной в четверь
дюйма, показанное на рис. 5.7, задается с помощью следующего выражения:
border: 0.25in s o l i d black

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

padding-left, padding-right, padding-top, padding-bottom


pt, pc, in, cm, mm | em, ex, px, %
Данные свойства позволяют задавать размеры левой, правой, верхней и нижней
частей заполненной области. Заметьте, что если граница отображает фон родитель­
ского элемента (обычно это элемент BODY), а для обрамления задается независимый
фон, то область, о которой идет речь, заполняется фоном, принадлежащим вклю­
чаемому элементу. Размеры могут быть заданы в стандартных единицах длины или в
процентах от размеров элемента. По умолчанию принимается значение 0. Отрица­
тельные значения не допускаются.

padding
Данное свойство позволяет задавать размеры верхней, правой, нижней и левой
частей заполненной области в рамках одной записи. Если для свойства padding
указано только одно значение, оно применяется для всех четырех частей области.
Если же заданы два или три значения, недостающий размер принимается равным
размеру противоположной части. Например, заполненная область шириной чет­
верть дюйма, показанная на рис. 5.7, задается с помощью следующего выражения:
padding: 0.25in
5.9. Изображения и плавающие элементы 163

Типы отображения блока


Большинство HTML-элементов могут содержать границы, обрамление и запол­
ненные области. Однако особенности интерпретации этих компонентов зависят от
того, включен ли текущий элемент в состав абзаца, является ли независимым элемен­
том блокового уровня или представляет собой часть списка. Свойство d i s p l a y по­
зволяет изменить порядок интерпретации блока.

display
block I inline | list-item | none
Данное свойство определяет, должен ли элемент отображаться в составе ограни­
ченного блока, выводиться как абзац, подобно элементам Р или PRE, либо должен
быть представлен как встроенный блок в составе другого блока. Допустимыми
значениями являются b l o c k , i n l i n e , l i s t - i t e m и none. Значение l i s t - i t e m
интерпретируется как b l o c k , за исключением того, что к пункту списка добавля­
ется маркер.

5.9. Изображения и плавающие элементы


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

width, height
auto I pt, pc, in, cm, mm | em, ex, px
Данные свойства задают фиксированные размеры элемента и обычно применяют­
ся к изображениям. Значения указываются в стандартных единицах длины, кроме
того, в качестве значения может быть использовано ключевое слово a u t o . Напри­
мер, тип "bullet" задается следующим образом:
IMG.bullet { width: 50рх; height: 50рх }
Ключевое слово a u t o применяется к изображениям, для которых задана либо ши­
рина, либо высота. Оно означает, что изображение должно быть масштабировано
с сохранением соотношения между исходной шириной и высотой.

float
none I left I right
Данное свойство располагает плавающие элементы по левой или правой границе с
обтеканием текста. Часто это свойство используется для выделения прописных
букв и для размещения плавающих изображений. Например, в листинге 5.10 пока­
зан код документа, в котором выделяется прописная буква "Т" размером 75 пунк­
тов. Внешний вид документа показан на рис. 5.8.
164 Глава 5. Каскадные листы стилей

Листинг5.10. Psalm23.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>The 23rd Psalm</TITLE>
<STYLE>
<! —
SPAN { float: left;
font-family: "Gushing Book";
font-size: 75pt }
-->
</STYLE>
</HEAD>
<BODY>
<H2 ALIGN="CENTER">
The 23rd Psalm (King James Version)</H2>
<SPAN>T</SPAN>he LORD is my shepherd; I shall not want.
He maketh me to lie down in green pastures: he leadeth me
beside the still waters. He restoreth my soul: he leadeth me
in the paths of righteousness for his name's sake. Yea,
though I walk through the valley of the shadow of death, I
will fear no evil: for thou art with me; thy rod and thy
staff they comfort me. Thou preparest a table before me in
the presence of mine enemies: thou anointest my head with oil;
my cup runneth over. Surely goodness and mercy shall follow me
all the days of my life: and I will dwell in the house of the
LORD forever.
</BODY>
</HTML>

шшшшшшв
file g(M ifiew fio £ofwnunic«ioi НФ

The 23rd Psalm


(King James Version)
he LORD is my

T
shepherd; I shall not
want. He maketh me to
lie down in green
pastures: he leadeth me
beside the still waters. He
restoreth my soul he
leadeth me in the paths of
righteousness for his name's sake
Yea, though I walk through the
уаДеу of the shadow of death, I will »j Рис. 5.8. Свойство f l o a t может быть
использовано для выделения прописных букв

clear
none I left I right | both
Данное свойство определяет, допустимы ли плавающие элементы слева или справа
от элемента. В качестве значений могут быть указаны l e f t (продолжить вывод
ниже плавающего элемента, расположенного слева), r i g h t (продолжить вывод
5.10. Свойства списков 165

ниже плавающего элемента, расположенного справа), b o t h (продолжить вывод


ниже любого из плавающих элементов) и п о п е (разрешить плавающие элементы).
Например, код Web-страницы (листинг 5.10), изображенной на рис. 5.8, можно
модифицировать так, чтобы после первого предложения начинался новый абзац.
Если для создания абзаца используется обычный дескриптор <Р>, то документ бу­
дет выглядеть так, как показано на рис. 5.9,а.

The 23 rd Psalm The 23rd Psalm


(King James Version) Й (King James Version)
he LORD is my he LORD is my

T
shepherd; I shall not shepherd; I shall not

^^^
•want.

He maketh me to lie
down m green pastures:
T want.

He maketh me to lie down in green


he leadeth me beside the pastures: he leadeth me beside the
still waters. He restoreth my soul: he still waters He restoreth my soul: he
leadeth me in the paths of leadeth me in the paths of
righteousness for his name's sake z! nfihteousiifsb L r b s name': salce d
Yea, though I walk through the (6)
(а)
Рис. 5.9. (a) По умолчанию текст, принадлежащий новому абзацу, продолжает
обтекание плавающего элемента; (б) новый абзац начинается ниже плавающего
элемента, определенного в предыдущем абзаце

Если же создать абзац с помощью дескриптора <Р S T Y L E = " c l e a r : l e f t " > , то


Web-страница будет выглядеть так, как показано на рис. 5.9,6, т.е. абзац начнется под
нижней границей плавающего элемента (буквы "Т").

5.10. Свойства списков


Каскадные листы стилей позволяют изменить ф о р м а т и р о в а н и е нумерованных
списков (0L), маркированных списков (UL) и списков определений (DL). Данные
свойства не обрабатываются Internet Explorer 3, а Netscape 4 поддерживает только
свойство l i s t - s t y l e - t y p e .

list-style-image
n o n e I иг\{гшя_файла)
Данное свойство позволяет задавать новые маркеры для списков. Используя это
свойство, следует помнить, что оно поддерживается только Internet Explorer. В ка­
честве значения задается URL либо ключевое слово попе. П р и м е р ы использования
l i s t - s t y l e - i m a g e приведены ниже. Первое выражение устанавливает в качестве
маркера по умолчанию для маркированного списка изображение, содержащееся в
файле d i a m o n d . g i f . Второе выражение задает класс s t a r , для которого маркиро­
ванные списки помечаются маркером в виде звездочки (файл s t a r . g i f ) .
UL { l i s t - s t y l e - i m a g e : u r l ( d i a m o n d . g i f ) }
UL.star { l i s t - s t y l e - i m a g e : u r l ( s t a r . g i f ) }
166 Глава 5. Каскадные листы стилей

Если броузер поддерживает анимационные GIF-файлы, их также можно использо­


вать в качестве маркеров. Броузер Netscape не поддерживает свойство l i s t -
style-image.

list-style-type
n o n e I disc | circle | square | decimal | upper-alpha | lower-alpha | upper-roman |
lower-roman
Д а н н о е свойство устанавливает маркер для списка в том случае, когда задано зна­
чение п о п е свойства l i s t - s t y l e - i m a g e (оно принимается по умолчанию для 0L
и DL). Допустимыми значениями являются d i s c (закрашенный круг), c i r c l e
(незакрашенный круг), s q u a r e , d e c i m a l (1, 2, 3 и т.д.), u p p e r - a l p h a (А, В, С
и т.д.), l o w e r - a l p h a (а, Ь, с и т.д.), u p p e r - r o m a n (I, II, III и т.д.), l o w e r - r o m a n (i,
ii, iii и т.д.) и n o n e .

list-style-position
outside I inside
Д а н н о е свойство, допустимыми значениями которого являются o u t s i d e (по
умолчанию) и i n s i d e , указывает, должен ли маркер располагаться в пределах аб­
заца ( i n s i d e ) или выступать влево ( o u t s i d e ) . Н и Netscape, ни Internet Explorer
не поддерживают свойство l i s t - s t y l e - p o s i t i o n .

list-style
Д а н н о е свойство позволяет задавать значения l i s t - s t y l e - i m a g e , list-style-
t y p e и l i s t - s t y l e - p o s i t i o n в одной записи.

5 . 1 1 . Стандартные единицы для


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

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

Таблица 5 . 1 . Абсолютные и относительные единицы длины


cm Сантиметры (абсолютная единица)
em Высота текущего ш р и ф т а (относительная единица)
ех Высота буквы "х" в текущем ш р и ф т е (относительная единица)
5.12. Слои 167

Окончание табл. 5 . 1 .
in Дюймы (абсолютная единица)
пил Миллиметры (абсолютная единица)
рс Пика; в дюйме 6 пика; в пика 12 пунктов (абсолютная единица)
pt Пункты; в дюйме 72 пункта (абсолютная единица)
рх Пиксели (относительная единица)

Цвет
Каскадные листы стилей позволяют задавать цвет любым из способов, описанных
в табл. 5.2.

Таблица 5.2. Форматы цвета в CSS1


имя цвета В качестве имени цвета используется одно из стандарт­
ных имен, перечисленных в табл. 1.1. Netscape и Internet
Explorer также поддерживают имена цветов X I 1
#RRGGBB Каждый из компонентов RR, GG и ВВ представляет собой
шестнадцатеричное число в диапазоне от 00 до FF
#RGB Д а н н ы й формат является сокращенным представлением
ф о р м а т а #RRGGBB. Н а п р и м е р , значение #OAF эквива­
л е н т н о значению # О OAAFF
rgb ( r r r , ggg,bbb) В этом формате каждый из компонентов r r r , ggg и
b b b представляет собой десятичное число в диапазоне от
О до 255
r g b {rrr%, ggg%,bbb%) В данном формате компоненты r r r , g g g и b b b являются
десятичными числами в диапазоне от О до 100

5.12. Слои
Netscape 4 поддерживает средство, которое п р и н я т о называть слоями. Слои по­
зволяют размещать элементы разметки в отдельных прямоугольных областях, а затем
располагать эти области в абсолютных и относительных позициях. Области могут пе­
рекрываться, в этом случае, определяя верхнюю область как прозрачную, можно ото­
бражать нижележащие данные. Слои дают возможность ф о р м и р о в а т ь перекрываю­
щиеся заголовки и панели, отображать текст в несколько колонок, аннотировать диа­
граммы и другие рисунки и создавать составные изображения, помещая прозрачные
картинки поверх других. Включая в состав документа JavaScript-сценарии, можно ди­
намически превращать видимые области в невидимые и наоборот. П р и н ц и п динами­
ческого изменения слоев подробно описан в главе 24, здесь же, оценивая преимуще­
ства слоев, достаточно помнить о такой возможности. Слои задаются в теле докумен­
та (раздел BODY) с помощью элементов LAYER и ILAYER. Для обеспечения соответ-
168 Глава 5. К а с к а д н ы е листы с т и л е й

СТВИЯ спецификации HTML 4.0 из Netscape 6 исключена поддержка элементов LAYER


и ILAYER. Internet Explorer поддерживает слои лишь посредством каскадных листов
стилей.
На з а м е т к у

Internet Explorer и Netscape 6 не поддерживают элементы LAYER И


ILAYER. Слои реализуются посредством листов стилей.

Определение слоев с помощью элементов LAYER


и/LAYER
Элемент: <LAYER ...> ... </LAYER>
<ILAYER ...> ... </1ЬАУЕ10
А т р и б у т ы : ABOVE, BACKGROUND, BELOW, BGCOLOR, CLIP, HEIGHT, ID, LEFT, ONBLUR,
ONFOCUS, ONLOAD, ONMOUSEOVER, ONMOUSEOUT, PAGEX, PAGEY, SRC, TOP, V I S I B I L I T Y ,
WIDTH,Z-INDEX
Элемент LAYER создает области и размещает их в абсолютных позициях по отноше­
нию к окн)' или родительскому слою. С помощью элемента LAYER можно сформировать
встроенные слои: области, включенные в текстовый поток. Альтернативный текст для
броузеров, не поддерживающих слои, помещается в состав элемента NOLAYER. Если
броузер поддерживает элемент LAYER, содержимое NOLAYER игнорируется.

ABOVE, BELOW, Z-INDEX


Обычно слои размещаются в том порядке, в котором они появляются в документе:
первый слой считается нижним, а остальные располагаются над ним. Данные атри­
буты позволяют изменить расположение слоев, принятое по умолчанию. Для кон­
кретного слоя может быть задан лишь один атрибут ABOVE, BELOW или Z-INDEX.
Значением атрибута Z-INDEX является положительное целое число, которое при­
сваивается слою. Слои с большими номерами располагаются поверх слоев с меньши­
ми номерами. В качестве значения атрибута ABOVE или BELOW задается идентифика­
т о р слоя, который должен быть расположен непосредственно над или под текущим
слоем. Использование этих слоев взывает у некоторых авторов затруднения. Так,
выражение <LAYER I D = " F o o " ABOVE="Bar"> означает, что слой, идентифициро­
ванный как Ваг, должен располагаться поверх слоя Foo, а не наоборот.

Внимание!

ABOVE И BELOW означают, что слой, на который ссылается соответст-


вующий атрибут, должен располагаться ниже или выше текущего слоя.
Выражение <LAYER ID="currentLayer" ABOVE="referencedLayer">
указывает на то, что слой currentLayer должен находиться ниже слоя
referencedLayer.

П р и м е р применения атриб)тов ABOVE и BELOW приведен в листинге 5.11. Внеш­


ний вид документа показан на рис. 5.10.
5 . 1 2 . Слои 169

Л и с т и н г 5 . 1 1 . Использование атрибутов ABOVE И BELOW

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Using ABOVE and BELOW</TITLE>
</HEAD>
<BODY>
<Hl>Using <CODE>ABOVE</CODE> and <C0DE>BEL0W</C0DE></H1>

<LAYER ID="Top" LEFT=60 TOP=120


WIDTH=500 HEIGHT=100 BGCOLOR="#F5DEB3">
This layer is on top, even though it appears
first in the HTML document.
</LAYER>

<LAYER ID="Bottom" ABOVE="Top" LEFT=10 TOP=70


WIDTH=500 HEIGHT=100 BGCOLOR="gray">
This layer is on the bottom, even though it appears
second in the HTML document.
</LAYER>

</BODY>
</HTML>

f ж Using ABOVE a n d BELOW - NetsGdf№ . .:.." :u^:-:'-^~-:,.<M^'Z^:'^^i^ ШШШ


File £cW ^(ew £ 0 £ommwrec«*o» Н Ф

:•'; ^' ~>r "'S rft *5^»> ^ -S ^ Q Ш Si\


Using ABOVE and BELOW

1 •1|дВ"'"'^<^ layer is on top, even thougii it appears first in Йхе HTML docTjment

IQI* -*.• ;DOGUrf»rtt D o n e ..„: ,:.^^*-,.. ...,. .,,i~ .:^:......, ^Я.: ^лШ-Ш
Рис. 5.10. А т р и б у т ы ABOVE, BELOW И Z - I N D E X ПОЗВОЛЯЮТ
изменять порядок следования слоев

BACKGROUND, BGCOLOR
По умолчанию слои создаются прозрачными. Ч т о б ы сделать слой непрозрачным,
надо задать ф о н о в о е изображение или цвет фона. Так, в предыдущем примере
(листинг 5.10 и рис. 5.10) с помощью атрибута BGCOLOR определялся цвет ф о н а для
каждого слоя. Если атрибут BGCOLOR задает н е п р о з р а ч н ы й ф о н , атрибут
BACKGROUND может использоваться для создания частично прозрачного фона, для
этого в качестве фонового должно быть задано п р о з р а ч н о е GIF-изображение.
170 Глава 5. К а с к а д н ы е листы с т и л е й

CLIP
Значением данного атрибута является набор целых чисел, разделенных запятыми,
которые определяют размеры области, занимаемой слоем. H T M L воспроизводит
всю область, но часть слоя может быть отсечена при выводе. Размеры задаются в
формате ''левая_граница, верхняя_граниг1,а, правая_граница, нижняя_грапица' либо в
формате ''правая_граница, нижняя_граница\ Последняя запись эквивалента "О, О,
правая_граниг^а, нижпяя_граница\

WIDTH, HEIGHT
Д а н н ы е атрибуты задают минимальную ширину и высоту слоя. Если атрибуты
WIDTH и HEIGHT не указаны, ширина и высота определяется содержимым слоя.
Заметьте, что WIDTH и HEIGHT — не максимальные, а минимальные размеры. П р и
необходимости ширина и высота увеличиваются, чтобы требуемая и н ф о р м а ц и я
могла разместиться в пределах области. В примере, приведенном в листинге 5.11 и
на рис. 5.10, атрибуты WIDTH и HEIGHT задавали размеры слоев.

ID
Данный атрибут присваивает слою имя. Это имя может быть использовано в качест­
ве значения атрибутов ABOVE и BELOW либо при выполнении сценария JavaScript.

LEFT, T O P , PAGEX, PAGEY


Для слоев, определенных посредством элемента LAYER, данные атрибуты указы­
вают позицию относительно включающего слоя (LEFT, ТОР) либо относительно
всей Web-страницы (PAGEX, PAGEY). Значения атрибутов задаются в пикселях. Ес­
ли эти атрибуты не указаны, слой располагается в текущей позиции на Web-
странице. В примере, приведенном в листинге 5.11 и на рис. 5.10, атрибуты LEFT и
ТОР использовались для определения позиции слоев.
Для встроенных слоев (ILAYER) значения атрибутов LEFT и ТОР задаются относи­
тельно текущей позиции на Web-странице. В листинге 5.12 атрибут ТОР использу­
ется для размещения каждого слова из предложения "Gently down the stream" ниже
предыдущего слова. Внешний вид документа показан на рис 5.11.

Л и с т и н г 5.12. Использование атрибута ТОР элемента I L A Y E R

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>Row, Row, Row Your Boat</TITLE>
</HEAD>
<BODY>
<IMG S R C = " i m a g e s / R o w b o a t . g i f " ALIGN="RIGHT">
<HR>
<B>Row, Row, Row Your Boat</B><BR>
Row, row, row y o u r boat<BR>
Gently
<ILAYER TOP=10>down</ILAYER>
<ILAYER TOP=20>the</ILAYER>
<ILAYER TOP=30>streain<BR>
5-12. Слои 171

Merrily, merrily, merrily, merrily<BR>


Life is but a dream<BR>
<HR>
</ILAYER>
</BODY>
</HTML>

te4iHHitHiffil'ff1 РШ11
£fe £cft ^lew fio £omfnunical« Цв^

шш^т^?^ ill *^- A ^^'-.if-

Row, Row, Row Your Boat


Row, row, row your boat
Gently
down
the
stream
Memly, memly, memly, merrily
Life IS but a dream

Рис. 5.11. Атрибут TOP элемента i LAYER позволяет перемещать


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

SRC
Д а н н ы й атрибут задает URL HTML-документа, который должен быть помещен в
указанный слой. Если в документе также определены слои, они рассматриваются
как д о ч е р н и е по отношению к текущему слою. Описанная возможность полезна в
тех случаях, когда необходимо включить фрагмент HTML-кода в несколько доку­
ментов, а также при поддержке Web-страницы, основная часть которой остается
неизменной, а один небольшой фрагмент часто изменяется. Так, например, вы
можете включить контактную и н ф о р м а ц и ю в состав Web-страницы, поместив вы­
ражение <LAYER S R C = " C o n t a c t - I n f o . h t m l " > < / L A Y E R > непосредственно пе­
ред закрывающим дескриптором </BODY>. Для включения в статический документ
динамически изменяющегося фрагмента можно использовать следующий фраг­
мент кода:
<Hl>Menu f o r J o e ' s D i n e r < / H l >
<ILAYER S R C = " B l u e - P l a t e - S p e c i a l . h t m l " > < / I L A Y E R >
<H2>Appetizers</H2> . . .
<H2>Main D i s h e s < / H 2 > . . .
<H2>Vegetables</H2> . . .

VISIBILITY
Д а н н ы й атрибут задает видимость слоя, т.е. определяет, должен ли слой отобра­
жаться в окне броузера. Допустимыми значениями являются SHOW, INHERIT и
HIDDEN. З н а ч е н и е SHOW указывает на то, что слой должен отображаться, значение
INHERIT устанавливает видимость слоя равной видимости родительского слоя, а
значение HIDDEN запрещает отображение. Скрытые слои трудно применять на
172 Глава 5. Каскадные листы стилей

статических Web-страницах, однако возможность запретить или разрешить ото­


бражение слоя можно использовать при создании JavaScript-сценариев.

ONBLUR, ONFOCUS, ONLOAD, ONMOUSEOVER, O N M O U S E O U T


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

Работа со слоями посредством листов стилей


Листы стилей поддерживают большинство возможностей (но не все), предостав­
ляемых элементами LAYER и ILAYER. Листы стилей не позволяют задавать с помощью
атрибута SRC внешний файл, кроме того, в листах стилей отсутствуют атрибуты, анало­
гичные PAGEX и PAGEY, посредством которых можно было бы размещать дочерние слои
независимо от расположения родительского слоя. Однако Netscape 4 обеспечивает бо­
лее полную поддержку средств работы со слоями посредством листов стилей и позволя­
ет добиться большей надежности по сравнению с использованием элементов LAYER и
ILAYER. Например, если цвет ф о н а для слоя задается с помощью листов стилей, ф о н
нижележащего слоя или Web-страницы виден на границе между абзацами, т.е. фоновые
изображения ведут себя так, как этого и следует ожидать. Заметьте также, что из
Netscape 6 удалены средства поддержки элементов LAYER и ILAYER. П р и работе со
слоями посредством листов стилей поддерживается позиционирование с использова­
нием стандартных единиц длины. Кроме того, соответствующие средства листов стилей
поддерживают как Netscape, так и Internet Explorer, что дает возможность создавать до­
кументы со слоями, которые корректно отображаются в обоих броузерах.
Слои должны описываться либо в заголовке с использованием идентификаторов,
определенных пользователем, либо в составе элемента, например <DIV STYLE=" . . . ">
(блоковый уровень) или <SPAN STYLE=". . . "> (текстовый уровень). П р и определе­
нии слоя должно задаваться свойство p o s i t i o n . Например, правило
#1ауег1 { p o s i t i o n : absolutes-
l e f t : 50рх; t o p : 7 5рх;
. . . }

примененное в элементе
<SPAN I D = " l a y e r l " >

</SPAN>
почти эквивалентно следующему выражению:
<LAYER I D = " l a y e r l " LEFT=50 ТОР=75 ...>

</LAYER>
Аналогично, правило
#1ауег2 { position: relative;
t o p : lOpx;
... }
5.12. Слои 173

примененное в элементе
<SPAN I D = " l a y e r 2 " >

</SPAN>
почти эквивалентно элементу, приведенному ниже.
<ILAYER I D = " l a y e r 2 " ТОР=10 ...>

</ILAYER>
В дополнение к стандартным атрибутам, допустимым в CSS1, при работе со слоями
поддерживаются следующие атрибуты.

clip
З н а ч е н и е данного атрибута задается в виде прямоугольной области, т.е. с помо­
щью выражения гect {верхняя_граница, правая_граница, нижпяя_грапица, ле-
вая_граница) либо с помощью ключевого слова a u t o (по умолчанию). Свойство
c l i p эквивалентно атрибуту CLIP элементов LAYER и ILAYER. Заметьте, что раз­
меры области, как и все другие величины, связанные с позиционированием слоев,
задаются с помощью стандартных единиц длины CSS. Помимо пикселей, исполь­
зуемых в LAYER и ILAYER, могут быть заданы пункты, дюймы и сантиметры.

left, t o p
Д а н н ы е свойства определяют верхний левый угол слоя в стандартных единицах
длины CSS. Эти свойства эквивалентны атрибутам LEFT и ТОР элементов LAYER и
ILAYER.

overflow
Д а н н о е свойство определяет поведение слоя в том случае, когда содержимое эле­
мента превышает высоту или ширину слоя. Значение попе (по умолчанию) указы­
вает на то, что содержимое должно отображаться обычным способом. З н а ч е н и е
c l i p задает отсечение, а значение s c r o l l указывает на то, что броузер должен
выполнить прокрутку, чтобы вместить содержимое.

position
Д а н н о е свойство может приобретать значения a b s o l u t e , r e l a t i v e и s t a t i c .
Эти значения соответствуют элементу LAYER, элементу ILAYER и обычным эле­
ментам, для которых не используются слои. П о умолчанию принимается значение
static.

visibility
Д а н н о е свойство определяет, должен ли слой быть видимым или скрытым. Допус­
тимыми значениями являются v i s i b l e , h i d d e n и i n h e r i t . Они соответствуют
нормально отображаемому элементу, скрытому элементу или элементу, наследую­
щему видимость родительского элемента. Д а н н о е свойство может использоваться
при динамическом о т о б р а ж е н и и элементов посредством JavaScript-сценариев.
174 Глава 5. Каскадные листы стилей

Подробно JavaScript-сценарии будут описаны в главе 24. В листинге 5.13 представ­


лен код простой Web-страницы, в которую включена форма, содержащая две
кнопки. Под кнопками расположены две невидимые области. Внешний вид Web-
страницы представлен на рис. 5.12. При активизации левой кнопки отображается
первый скрытый слой (рис. 5.13), а отображение второго слоя, если это необхо­
димо, запрещается. Модель документа в Internet Explorer и Netscape отличается,
поэтому для обеспечения межплатформенной совместимости добавлена вспомо­
гательная JavaScript-функция d i s p l a y .

Листинг 5.13. Динамическое изменение видимости слоев

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Changing Visibility Dynamically</TITLE>
<STYLE>
<! —
#layerl { position: absolute; left: 0.25in; top: 1.5in;
color: black; background-color: #F5DEB3;
visibility: hidden }
#layer2 { position: absolute; left: 0.25in; top: l.Sin;
color: #F5DEB3; background-color: black;
visibility: hidden }
HI { text-align: center;
font-family: Arial }
FORM { text-align: center }
-->
</STYLE>
<SCRIPT TYPE="text/javascript">
<! —
function display(valuel,value2){
if(document.layers) { // Проверка для Netscape,
document.layers.layerl.visibility = valuel;
document.layers.Iayer2.visibility = value2;
} else {
document.all.layerl.style.visibility = valuel;
document.all.Iayer2.style.visibility = value2;
}
}
// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>Changing Visibility Dynamically</Hl>
<FORM>
<INPUT TYPE="BUTTON" VALUE="Show Layerl"
onClick=!"display('visible' , 'hidden') ">
<INPUT TYPE="BUTTON" VALUE="Show Layer2"
onClick="display('hidden','visible')">
</FORM>
<DIV ID="layerl">
<Hl>This is layerl.</Hl>
</DIV>
5.12. Слои 175

<DIV ID="layer2">
<Hl>This is Iayer2.</Hl>
</DIV>
</BODY>
</HTML>

' Changing visflbitily DfnanwcMiy - Nel«cepe


file £dit VJaw До £omniur«ceJ£3r М Ф
Eaai

Changing Visibility
Dynamically

^!^:^^r "" ^С^ал*и!*"б'о»»Щ

Рис. 5.12. Web-страница, содержащая JavaScript-сце-


нарий и два скрытых слоя

Changing Visibility
Dynamically

This is layeM.
^д^-^ =»jl^.- <

Рис. 5. УЗ. При активизации кнопки отображается скры­


тый слой

width, height
Д а н н ы е свойства определяют размеры слоев. О н и выполняют те же функции, что
атрибуты WIDTH и HEIGHT элементов LAYER и ILAYER.

z-index
В о б ы ч н ы х условиях слои располагаются один над другим в том порядке, в кото­
ром они встречаются в HTML-документе. Свойство z - i n d e x изменяет порядок
следования слоев. Значением свойства является целое число, определяющее но­
мер слоя. Слои с меньшими номерами располагаются ниже слоев с большими но­
мерами. Данное свойство эквивалентно атрибуту Z-INDEX элементов LAYER и
ILAYER.
176 Глава 5. Каскадные листы стилей

5.13. Резюме
Каскадные листы стилей представляют собой мощный инструмент форматирова­
ния Web-страниц. П р и работе с документами H T M L 4.0 листы стилей предпочтитель­
нее др)тих средств. Листы стилей позволяют задавать ш р и ф т ы , цвет ф о н а и изобра­
жения для различных разделов текста, определять плавающие элементы, границы и
отступы. Листы стилей также дают возможность ф о р м а т и р о в а т ь списки. Помимо но­
вых возможностей, связанных с выводом текста, каскадные листы стилей позволяют
добиться, чтобы документ имел одинаковый вид в различных броузерах. Благодаря
использованию листов стилей достигается однотипная обработка слоев в Netscape и
Internet Explorer.
Несмотря на обилие возможностей, H T M L все же представляет собой не язык
программирования, а язык разметки, поэтому набор приложений, которые могут
быть созданы посредством HTML, ограничен. Язык Java позволяет создавать про­
граммы, которые включаются в состав Web-страниц и выполняются в среде броузера.
Сфера применения Java не исчерпывается программами, работающими в составе
Web-страниц. Java представляет собой язык программирования общего назначения.
Рассмотрению Java-технологии посвящена часть II данной книги.
-JlEJS^-.

ПРОГРАММИ­
РОВАНИЕ
НА ЯЗЫКЕ JAVA
Глава 6. Общие сведения о языке Java
Глава 7. Объектно-ориентированное программирование на Java
Глава 8. Синтаксис Java
Глава 9. Аплеты и основные действия с графикой
Глава 10. Java 2D: графика в Java 2
Глава 11. События, связанные с мышью и клавиатурой
Глава 12. Диспетчеры компоновки
Глава 13. КомпонентыAWT
Глава 14. Основы Swing
Глава 15. Расширенные средства Swing
Глава 16. Использование потоков
Глава 17. Сетевое программирование
ОБЩИЕ СВЕДЕНИЯ
О ЯЗЫКЕ JAVA

В ЭТОЙ главе...

• Уникальные возможности Java. Средства,


предоставляемые разработчикам.
• Java: мифы и реальность.
• Этапы развития Java.
• Программное обеспечение и документация,
необходимые для работы.
• Компиляция и запуск Java-программы.
• Примеры простых Java-программ.
U~y\zJsJ:EJ

ava— это язык программирования, во многом напоминающий C++. Java использует­

J ся для написания приложений общего назначения, для создания программ, вклю­


чаемых в состав WWW-страниц, а также для построения корпоративных приложе­
ний и организации узлов электронной коммерции. Об этом языке положительно от­
зываются как программисты, так и администраторы систем. Компании IBM и Oracle
уделяют Java настолько большое внимание, что первым шагом участия в программе
Enterprise Developer является получение сертификата (см. h t t p : //www-4 . i b m . com/
software/ad/certify/adedserv.html
и http://education.oracle.com/ c e r t i f i c a t i o n / j avatrack.html).
Популярность Java обусловлена следующими причинами.

• Наличие интуитивно понятных средств сетевого обмена, в частности взаимо­


действие с Web.
• Наличие кроссплатформенной поддержки.
• Простота языка.
• Поддержка объектно-ориентированных средств.
• Наличие большого количества стандартных библиотек.
Поскольку Java-программы могут выполняться в среде Windows и обеспечивают
автоматическую "сборку мусора", н е к о т о р ы е энтузиасты утверждают, что Java— это
"единственный язык", на который должны перевести свои приложения все програм­
мисты. Скептики критикуют Java за недостатки, однако при беспристрастном рас­
смотрении становится ясно, что эти недостатки надуманы, или, по крайней мере,
преувеличены. Одним из таких заблуждений является утверждение, будто язык Java
предназначен только для Web.
Если вы еще не решили, какая версия Java более всего удовлетворяет вашим нуж­
дам, далее в этой главе будут рассмотрены различия между реализациями данного
языка. Если по каким-то причинам на вашем компьютере еще не установлен Java, про-
180 Глава 6. Общие сведения о языке Java

читав эту главу, вы узнаете, где можно найти необходимое программное обеспечение
и документацию. Наконец, в этой главе будут приведены п р и м е р ы простых программ,
которые помогут вам начать изучение Java.
Если вы считаете Java аббревиатурой, вы ошибаетесь. Дело в том, что в Америке
слово "Java" — почти синоним слова "кофе". Этим названием компания Sun заменила
имя Oak, которое конфликтовало с существующим продуктом. Со словом Java ассо­
циируется нечто тонизирующее и стимулирующее к работе.

6 . 1 . Возможности Java
Как бы восторженно ни отзывались разработчики о Java, его нельзя считать уни­
кальным. Действительно, хотя Java — прекрасный язык, он никак не является набором
гениальных решений, которые никогда не встречались прежде. Многие решения уже
были реализованы в других языках. Однако в Java были объединены стандартные вы­
разительные средства (синтаксис C/C++), богатые возможности проблемно-
ориентированных языков (автоматическое управление памятью и интерпретация
байтового кода) и набор API для разработки корпоративных систем. В последующих
разделах описаны некоторые наиболее важные характеристики Java.

Средства сетевого взаимодействия и работа в Web


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

Безопасность выполнения программ


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

Доставка п р о г р а м м н о г о о б е с п е ч е н и я ч е р е з Web
Java-аплеты поддерживаются в большинстве реализаций Netscape и Internet
Explorer, следовательно, могут выполняться практически в любой из существую­
щих операционных систем. Использование аплетов становится причиной рас­
смотрения броузера не только как программы, предназначенной для копирования
и отображения документа, но также как среды доставки и выполнения программ­
ного кода. Теперь, если вы работаете с приложением, которое подвергается час­
тым модификациям, у вас нет необходимости следить за выходом новой версии
программы и устанавливать ее. П о существу, вам не надо ничего инсталлировать,
достаточно создать в броузере закладку на Web-страницу, содержащую требуемый
6 . 1 . Возможности Java 181

аплет. Таким образом, обновление версий программы перестает быть обязанно­


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

Рис. 6.1. Аплет, разработаннь1й NASA, который отображает текущее


состояние телескопа

Простота применения сетевой библиотеки Java


Сетевгш библиотека Java используется приблизительно одинаково во всех опера­
ционных системах. Благодаря простоте сетевых средств появилась возможность
отказаться от инструментов типа мастер. Создание сетевого соединения на сторо­
не клиента или на стороне сервера в Java происходит значительно проще по срав­
нению с гнездами Berkeley или POSIX Transport Layer Interface. Кроме того, Java
поддерживает протокол HTTP, позволяя копировать файлы из Web и обращаться
к HTTP, не создавая непосредственно гнезд. На рис. 6.2 показан аплет, реализую-
182 Глава 6. Общие сведения о языке Java

щий Web-интерфейс для настройки портов Ethernet-коммутатора Cisco Catalyst


6000. HTTP-сервер, выполняющийся на коммутаторе Catalyst, позволяет конфигу­
рировать устройство посредством броузера, не прибегая к инструкциям, задавае­
мым из командной строки.

Рис. 6.2, Аплет Cisco OpenView, предназначенный для настройки Ethernet-коммутатора


Catalyst 6000 (данный материал опубликован с разрешения Cisco Systems, Inc.)
6 . 1 . Возможности Java 183

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


Начиная с 1995 г. компания Sun предлагает многочисленные библиотеки и расши­
р е н и я Java, предназначенные для реализации решений уровня предприятия. Java
поддерживает средства RMI (Remote Method Invocation — вызов удаленных мето­
дов), позволяющие обращаться к методам объектов, размещающихся на удаленных
узлах. RMI также дает возможность передавать произвольные Java-объекты по се­
ти. Пользуясь механизмом Java-сервлетов, можно создавать приложения, выпол­
няющиеся на стороне сервера, и создавать объекты сеанса для отслеживания по­
следовательных обращений одного и того же пользователя к приложению. JDBC
обеспечивает стандартный и н т е р ф е й с с различными базами данных и позволяет
аплетам и сервлетам обращаться к базам, минуя и н т е р ф е й с ы CGI. К о м п о н е н т ы En­
terprise JavaBeans, доступные из сервлетов и JavaServer Pages, дают возможность
реализовывать бизнес-логику в многосвязной архитектуре.

Кроссплатформенная поддержка Java


Язык Java с самого начала разрабатывался так, чтобы программы, написанные на
нем, были полностью переносимыми, т.е. могли без изменений быть запущены на
любой платформе. Каким образом эта задача была решена? Переносимость Java обес­
печивается тремя основными особенностями данного языка.
И с х о д н ы й т е к с т Java компилируется в машинно-независимый б а й т о в ы й к о д
Процесс компиляции и выполнения Java-программ состоит из двух этапов. Н а пер­
вом этапе исходный код Java компилируется в байтовый код, предназначенный для
выполнения на абстрактной виртуальной машине Java (JVM — Java Virtual
Machine). H a втором этапе Java-код запускается в среде выполнения. Среда выпол­
нения представляет собой либо и н т е р п р е т а т о р (эмулятор JVM), либо компилятор
J I T (Just In T i m e ) , который сначала преобразует байтовый код в исполняемую про­
грамму для конкретной архитектуры, а затем запускает ее на выполнение. Основ­
ное преимущество данного процесса состоит в том, что описанные этапы могут
выполняться на различных платформах. Исходный код может быть скомпилиро­
ван с помощью компилятора Borland на машине под управлением Windows 2000, а
полученный байтовый код запущен на Macintosh в среде выполнения, разработан­
ной Apple, либо на Solaris в среде выполнения, созданной Sun. В качестве примера
на рис. 6.3 представлен кроссплатформенный пакет подготовки документов
StarOffice, к о т о р ы й может выполняться в системах Windows, Solaris и Linux.
В большинстве современных Web-броузеров реализована виртуальная машина
Java, что позволяет авторам Web-страниц включать в них байтовый код аплетов.

Java п р е д о с т а в л я е т п е р е н о с и м у ю г р а ф и ч е с к у ю б и б л и о т е к у
Главным фактором, препятствующим переносимости большинства программных
систем, является пользовательский и н т е р ф е й с . Многие разработчики предпочи­
тают стандартным кроссплатформенным графическим средствам оконную систе­
му, ориентированную на конкретную платформу. Следуя подобному подходу, при
переходе на другую платформу компоненты программы, реализующие пользова­
тельский и н т е р ф е й с , приходится переписывать заново. Создатели Java исходили
из того, что переносимый язык должен включать стандартную графическую биб­
лиотеку, поэтому в реализацию Java компания Sun Microsystems включила AWT
184 Глава 6. Общие сведения о языке Java

(Abstract Window Toolkit — инструмент для реализации абстрактных оконных сис­


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

l i ^ S t a r t t f T t c e S ^ -iLrofiS-LanauaQedtittii:.-:;" ^ ':г ^:^'v'' '#::';'V/::':<riiwi:iKyH^^^^^B


1 Е«е EdK i^iew [rwert 'F^mA loots Cata ЙЗгккм» tNp
A i „ . B. _ 1 С D . . i _ E F . [ .<3. ..J „ H ^

JHU/APL T N O / U T w e n t e IRIT U Tampere CWI


0 0 68 0 58 0 62 0 68 0 5
0 1 0 63 0 51 0 46 0 53 0 41
V -А J 0 2 0 54 0 47 0 43 0 43 0 32

ш 76 ^\ 0 3
0 4
0 51
0 47
0 41
0 36
0 38
0 33
0 33
0 28
0 29
0 25
8 0 5 0 43 0 32 0 32 0 26 0 23
06 0 37 026 0 22 0 19 02
__ 9 _ 0 7 0 33 0 22 0 19 0 16 0 16
08 03 021 0.18 0 13 0.13
~ii"' 0.9 0.2 0.14 0.12 0.1 0.1
1 n^6 0 1 0.08 0.07 0.08
; ;М
' 13 1
И ;

; ; '^z
А Cross Language Retrieval
'• Z
г,; ""А liil \ JHU/APL
19 "чХц^
: В^ ' ^ , TNO/UTwente
" 20 0.6-
VIRIT
21
Ч UTartpere
0 5-
''^CWI

O 0.4-
О

2 03-
29'1
30
02-
31

__33j 0 1-

()

л
IMI 0.1 0.2 0.3 0,4 05 06 0.7 0.8 0.9

37 Recall

3%ii: f \ S h e e t i y Sheet2 / S h e e t s / '

М^^ J M l^lCross-Lang... if*


h2:36PM

Рис. 6.3. Кроссплатформенный пакет StarOffice 5.2, предназначенный


для обработки документов, полностью реализован на Java. StarOffice"^^
является зарегистрированной торговой маркой Sun Microsystems, Inc.

Реализуя платформу Java 2, Sun включила в ее состав Swing— графический пакет,


обеспечивающий большие возможности по сравнению с AWT и позволяющий соз­
давать высококачественные пользовательские и н т е р ф е й с ы . Swing рекомендуется
использовать при разработке графических программ за исключением аплетов,
включаемых в Web-страницы. П р и ч и н а в том, что непосредственную поддержку
Swing обеспечивает только Netscape 6. В других версиях Netscape и Internet
Explorer для этой цели приходится устанавливать дополнительные модули.
На рис. 6.4 показан графический пользовательский и н т е р ф е й с аплета, разрабо­
танный NASA. П р и ч и н о й создания этого аплета явилась необходимость предос­
тавления данных аппарата Pathfinder большому числу пользователей, работающих
на разных платформах.
6 . 1 . Возможности Java 185

|^ЩД|ЩД| H^joixil

_J__T ^

;^^Ш)^
~^чй

. ;-|3
v'^^.o>2t3isyT^>y^.:'?^^"^^^^^^
P^»»#Btfe^v#^^ "^v;>;- :-:л;;Г^Й=^^:;^|

m^\ nl4Q||¥®|ffl|^|Al
Rovef:|fOcky7 3 Оа1в:[и^?1 3 Cyde;[l 3 Map:|Eteve(ion 3

Рис. 6.4. Web-интерфейс,


разработанный Jet Propulsion
Lab и используемый для
Pointer Kicxfc отображения данных Mars
Pathfinder
В Java о т с у т с т в у ю т я з ы к о в ы е к о н с т р у к ц и и , с о з д а ю щ и е т р у д н о с т и
при переносе программ
В спецификации Java строго определены размеры простых типов данных, таких
как i n t , b o o l e a n и d o u b l e . Этим Java отличается от большинства других языков,
в которых размеры типов данных зависят от конкретной платформы. Java-прог­
раммы также не зависят от деталей реализации объектов, например от размеще­
ния элементов внутри объекта или от объема используемой памяти (в Java нет не­
обходимости в применении оператора s i z e o f , присутствующего в С/С+-ь). В Java
даже отсутствуют ссылки на локальную файловую систему, вместо этого использу­
ются такие абстрактные понятия, как имена классов и имена пакетов.
186 Глава 6. Общие сведения о языке Java

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

Автоматическое управление памятью


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

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

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


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

Все функции связаны с объектами


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

Практически все типы данных являются объектами


В некоторых объектно-ориентированных языках не все т и п ы данных представля­
ют собой классы. Строки, массивы, структуры, файлы, гнезда и другие типы не
6 . 1 . Возможности Java 187

обязательно являются объектами и их обработка может отличаться от обработки


объектов, определенных пользователем. В Java, напротив, все сложные т и п ы дан­
ных— это объекты, имеющие общего "предка"— класс O b j e c t . Такой подход по­
зволяет создавать массивы и другие наборы, состоящие из разнородных т и п о в
данных. Поскольку в Java существуют также простые типы данных ( i n t , d o u b l e ,
b o o l e a n , c h a r и т.д.), для удобства работы с ними созданы своеобразные
"оболочки" — объекты I n t e g e r , D o u b l e , B o o l e a n , C h a r a c t e r и т.д. П р о с т ы е ти­
пы могут быть преобразованы в объекты путем вызова специальных методов.

Стандартные библиотеки Java


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

• создавать структуры данных и использовать их;


• выполнять различные действия со строками и потоками;
• сохранять объекты (в том числе графические) на диске для последующего их
восстановления;
• использовать числа с ф и к с и р о в а н н о й точкой произвольной точности;
• вызывать удаленные Java-объекты;
• взаимодействовать с реляционными базами данных;
• организовать многопотоковые вычисления.
Наличие большого количества Java-библиотек позволят создавать п р и л о ж е н и я на
Java, не используя библиотек, специфических для конкретной о п е р а ц и о н н о й систе­
мы. П р и м е р такого приложения приведен на рис. 6.5.
Обилие встроенных средств не всегда однозначно воспринимается как положи­
тельное явление. С одной стропы, большое количество стандартных инструментов,
обеспечивающих переносимость, позволяет выбрать библиотеки, необходимые для
конкретного приложения. С другой с т о р о н ы , объем сведений о Java в целом настоль­
ко велик, что перспектива изучить соответствующие средства может испугать начи­
нающего программиста. К счастью, нет никакой необходимости изучать все возмож­
ности Java. Следует лишь освоить порядок создания и использования объектов (см.
главу 7), а также разобраться в основных языковых конструкциях (см. главу 8). Ос­
тальные разделы Java практически независимы один от другого. Поскольку классы
Java подгружаются динамически, объем исполняемой системы Java и скомпилирован­
ных классов не зависит от объема средств поддержки данного языка. Ч т о б ы добиться
успеха в написании программ определенного типа, нет необходимости изучать все
особенности Java. Если вы собираетесь разработать сервер, вам не обязательно знать
средства создания пользовательского интерфейса, поэтому вы можете отложить изу­
чение AWT (глава 13) и Swing (главы 14 и 15) до тех пор, пока соответствующие сред­
ства действительно не потребуются вам. Аналогично, если вы создаете о б ы ч н ы й ап-
лет, вам не понадобятся сведения об организации сетевого взаимодействия. Присту-
188 Глава 6. Общие сведения о языке Java

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

Н'НТДРДВ!

'~&ш <2ащт'^0тщг$^^Ш: ;^М±шМЁйШ^^1^<


http //md dmso mil/melb(n/iava_query
~вц 3
??>f|%vlgj;?S:-.|;p^|j^
Mijipp!i(iH|irtii>jffMiij?i »»'€'l*?i!iigWS^SW

J96.48 (W 3|23.039

WmaitJmjm X RentMTalH.

j^fe^^^^^Ei
ClicK to start йешш a ieeioti.

ir^
;^App$et3tdrtsd \^i г.ГТЧт'ь^'^ •
Рис. 6.5. Java-интерфейс к Master Environmental Library ( M E L ) ~ биб­
лиотеке для получения географических данных. Java-аплет предостав­
ляет описание выбранной области

6.2. Java: мифы и реальность


Когда Java впервые заявил о себе в 1995 г., многие скоропалительные выводы о ха­
рактере этого языка оказались ложными. Поскольку неверные представления об осо­
бенностях Java до сих пор имеют место, целесообразно посвятить раздел данной гла­
вы их опровержению.
6.2. Java: мифы и реальность 189

Java предназначен исключительно для Web


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

Кроссплатформенная поддержка Java


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

J a v a - п р и л о ж е н и я могут о б р а щ а т ь с я к л о к а л ь н ы м , н е п е р е н о с и м ы м
программам
Метод e x e c класса R u n t i m e позволяет Java-приложениям вызывать локальные
программы. Кроме того, платформенно-зависимый и н т е р ф е й с дает возможность
объединять программы, написанные на Java и С. Вызов локальных п р и л о ж е н и й
делает Java-программы полностью непереносимыми, а объединение с программа­
ми на С обеспечивает переносимость в той мере, в которой ее позволяют реализо­
вать связанные программы на С. Поэтому справедливо будет утверждать, что при­
л о ж е н и е на Java непереносимо лишь потому, что непереносима связанная с ней
программа. Однако такие приложения уже нельзя рассматривать как "чистые" Java-
программы. Несмотря на то что при написании ряда программ удастся избежать
обращения к платформенно-зависимым компонентам, в некоторых случаях необ­
ходимо форматировать диск, получать сведения о пользователях, зарегистриро­
ванных в системе, либо организовать взаимодействие с существующими приложе­
ниями, написанными на С или C++. Н е следует думать, что платформенно-
зависимых операций или взаимодействия с существующими компонентами надо
избегать. Напротив, возможность обращения к локальным средствам часто бывает
важнее переносимости. Плохо, если разработчик при создании программы не от­
дает себе отчет в том, что на определенном этапе она перестала быть переноси­
мой. Поэтому, используя библиотеки независимых производителей, будьте осто­
р о ж н ы и старайтесь выяснить, какая часть библиотеки представляет собой
"чистые" Java-средства, а какая часть зависит от платформы.
190 Глава 6. Общие сведения о языке Java

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


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

Графические библиотеки могут вести себя по-разному в различных системах


В первых реализациях Java разработчики приняли решение о том, что внешний вид
графических элементов должен по возможности совпадать с внешним видом эле­
ментов локальной оконной системы. Такой подход был во многом оправдан, по­
скольку, например, пользователи Macintosh категорически отказывались работать с
приложениями, предоставляющими интерфейс, типичный для Windows 2000. Одна­
ко оформление интерфейсных элементов Abstract Window Toolkit в стиле текущей
операционной системы привело к тому, что размеры полос прокрутки, кнопок, по­
лей ввода текста и других компонентов слегка изменялись при переходе от одной
платформы к другой. Несмотря на то что размеры элементов могут быть согласова­
ны путем правильного выбора диспетчера компоновки, специалисты Sun пришли к
решению, что для обеспечения реальной переносимости программ зависимость
графических компонентов от операционной системы должна быть устранена.
Для платформы Java 2 компания Sun предложила графический пакет Swing, в ко­
тором отображение элементов не зависит от операционной системы. В результате
графический интерфейс, реализованный с использованием Swing, стал полностью
переносимым. К сожалению, в настоящее время большинство броузеров может под­
держивать Swing только при наличии дополнительных модулей. Компоненты AWT,
напротив, поддерживаются практически всеми броузерами.

Простота Java
Несколькими страницами выше было сказано, что Java — простой язык. Об этом
стоит поговорить особо.
Дело в том, что простота— относительное понятие. По сравнению с С и C++ син­
таксические конструкции Java кажутся простыми в изучении и применении. Разра­
ботчики, которым довелось выяснять, почему не освобождается фрагмент памяти
6.2. Java: мифы и реальность 191

или откуда берется то или иное значение указателя, скажут, что автоматическая
"сборка мусора" освобождает программиста практически от всей работы. Однако не
следует забьщать, что Java — это все же язык программирования, а программирование
никогда не было простым делом. Тот, для кого основным занятием было создание
HTML-документов, скажет, что Java — чрезвычайно сложный язык. Из-за возрастаю­
щего количества стандартов и библиотек независимых производителей становится
все труднее следить за их появлением и изучать их.
Не говоря о том, что программирование — нелегкая работа сама по себе, программи­
сты постоянно стремятся решать более сложные задачи. Так, например, используя дру­
гие языки, только компьютерные гуру берутся за создание систем "клиент/сервер" или
многопотоковых приложений. В Java решением подобных задач часто занимаются про­
граммисты среднего уровня. Хорошо это или плохо? С одной стороны, разработчики
могут создавать программы, недоступные им ранее. С другой стороны, при написании и
отладке таких программ неизбежно возникают дополнительные трудности.
Таким образом, язык Java совсем не прост. Однако синтаксис языка понять доста­
точно легко, а тот факт, что компоненты Java можно изучать независимо друг от дру­
га, обусловливает привлекательность Java для программистов.

Java — объектно-ориентированный язык (для тех,


кто придерживается соответствующего подхода)
"Разве это не так?" — спросите вы. Дело в том, что программирование можно рас­
сматривать с разных точек зрения. Для некоторых программирование — это религия
и основная задача, как считают они, — проповедовать и обращать новичков "в свою
веру". Таких программистов отличает абсолютная верность конкретным операцион­
ным системам и языкам программирования и полное неприятие других. Однако вряд
ли это идеальная позиция. Наверное, лучше подходить к созданию программ "с точки
зрения плотника", который применяет по мере необходимости самые разные инст­
рументы. Одни инструменты используются часто, другие предназначены для выпол­
нения специфических задач. С часто используемыми инструментами можно сравнить
объектно-ориентированные средства, именно они составляют основу "инструмен-
тальнного набора" разработчика. Однако функциональное и структурное программи­
рование, программы, управляемые событиями, принцип "разделяй и властвуй" также
имеют право на существование и используются для решения конкретных задач. Объ­
ектно-ориентированный подход дополняет одни принципы программирования и
противоречит другим.
В основу Java положены объектно-ориентированные средства, что, несомненно,
является правильным выбором. Однако отказываться от применения некоторой тех­
нологии только потому, что она не соответствует объектно-ориентированному под­
ходу, по меньшей мере, неразумно.

Java — язык для разработки различных типов


программ
Java — язык общего назначения. Для решения ряда задач -- это прекрасный и даже
наилучший инструмент. Однако не следует думать, что Java одинаково хорошо подхо­
дит для написания любых программ. Иногда утилиты для работы в системе UNIX
192 Глава 6. Общие сведения о языке Java

удобнее создавать на С, для Windows-программ часто используется Visual Basic, а для


Web-приложений — JavaScript или VBScript. Продолжая аналогию с инструментами
плотника, можно сказать, что хороший специалист представляет себе преимущества
и недостатки различных языков и выбирает наиболее подходящий для решения кон­
кретной задачи. Однако следует заметить, что переносимость и способность взаимо­
действовать с другими компонентами системы являются важными характеристиками
любой программы. Поэтому в ряде случаев программисты отдают предпочтение Java
даже для тех программ, для которых использование другого языка было бы связано с
меньшими усилиями. Ч т о б ы правильно выбрать инструмент разработки, необходимо
иметь определенный опыт программирования.

6.3. Версии Java


Версия Java 1.0 была реализована компанией Sun Microsystems в 1995 г. В 1996 г.
компания Netscape реализовала виртуальную машину Java в броузере Netscape 2.0. Это
был первый ш и р о к о распространенный броузер, поддерживающий Java-аплеты. По­
сле незначительной доработки была выпущена версия Java 1.02; именно ее большин­
ство программистов называли Java 1.0. В начале 1997 г. компания Sun разработала
версию Java 1.1, в к о т о р о й был предусмотрен ряд новых возможностей.

• Новая модель обработки событий, основанная на использовании объектов про­


слушивания.
• RMI (Remote Method Invocation — вызов удаленных методов) и сериализация
объектов.
• Поддержка внутренних и анонимных классов.
• Поддержка целых чисел и чисел с плавающей точкой произвольной точности.
• JDBC (Java Database Connectivity) API для взаимодействия с р е л я ц и о н н ы м и ба­
зами данных.
• Компоненты JavaBeans (Java-ответ на появление ActiveX).
• Аплеты, заверенные ц и ф р о в о й подписью, для использования расширенного
набора привилегий (вместо подхода "все или ничего", применяемого при рабо­
те с дополнительными модулями и ActiveX).
П р и разработке аплетов лучше всего ориентироваться на Java 1.1 API, поскольку
именно этот и н т е р ф е й с поддерживается Netscape 4.06 и Internet Explorer 4.0, а также
более поздними версиями этих броузеров. Если создаваемые вами аплеты используют
возможности, предоставляемые новыми версиями Java, они могут н е к о р р е к т н о рабо­
тать в броузерах некоторых пользователей.
Платформа Java 2, поддерживаемая инструментальным пакетом JDK 1.2 (Java De­
velopment Kit 1.2), была реализована в декабре 1998 г. Н и ж е перечислены новые
средства, включенные B J D K 1.2.
• Компоненты пользовательского и н т е р ф е й с а Swing, полностью реализованные
средствами Java.
• Java 2D для профессиональной высококачественной двухмерной графики.
6.3. Версии Java 193

• Collection Framework с поддержкой таких структур данных, как связанные спи­


ски, деревья и множества.
• Аудиорасширения для поддержки форматов . wav, , a i f f, . a u , . m i d i и . rmf.
• Вывод на печать графических объектов.
• Java IDL API, с помощью которого в Java реализуется поддержка CORBA.
Весной 2000 г. Sun выпустила JDK 1.3. В этой версии были устранены некоторые
недостатки и реализованы новые средства API. Среди выполненных доработок наи­
более существенными можно считать следующие.

• Java Naming and Directory Interface (JNDI) — служба каталогов, предназначенная


для регистрации и поиска ресурсов (объектов).
• RMI-IIOP — протокол взаимодействия с распределенными клиентами, напи­
санными на CORBA-совместимом языке.
Теперь, когда перечень версий и пакетов, поддерживающих их, приобрел вн}тпи-
тельные размеры, следует заметить, что на самом деле JDK 1.2 и JDK 1.3 соответствуют
платформе Java 2, Standard Edition. Наряду со Standard Edition Sun предложила Java 2,
Enterprise Edition, в рамках которой были реализованы следующие возможности.

• Java-сервлеты и Jav2iServer Pages — ответ Sun на появление Microsoft Active


Sei-ver Pages и ColdFusion.
• Компоненты Enterprise JavaBeans для реализации бизнес-логики и построения
програхмм, выполняющихся на стороне сервера.
• Расширенные возможности просмотра результатов, предоставляемых средст­
вами JDBC.
• Использование JavaMail для передачи и приема почты посредством протоколов
STP, РОРЗ или IMAP4.
• JAXP для разбора XML-документов.
• Использование Java Message Service для асинхронного взаимодействия корпо­
ративных приложений.

Какой версии отдать предпочтение


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

• А п л е т ы . Для создания аплетов лучше всего выбирать Java Development Kit 1.1
(JDK 1.1, последней реализацией которого была JDK 1.1.8_005). Netscape 4.06,
Internet Explorer 4.01 и даже более поздние версии этих броузеров без допол­
нительных модулей поддерживают только версию 1.1 и более ранние версии
Java. Исключением является только Netscape 6, который, однако, не поддержи­
вает JDK 1.3. Распространяя аплеты по Internet, вы не можете знать, с какими
194 Глава 6. Общие сведения о языке Java

броузерами работают пользователями, для которых создаются аплеты, поэто­


му, приступая к созданию аплета, лучше ориентироваться naJDK 1.1 API.
• Приложения. Для создания независимых приложений можно воспользоваться
JDK 1.3, известным также под названием Java SDK, Standard Edition, Version 3.0.
Если вы создаете приложения для работы на стороне сервера, взаимодейст­
вующие с программами других производителей, выбор версии JDK определяет­
ся возможностями, которые предоставляют эти программы.
Итак, наилучший выбор для вас— использовать JDK 1.3, но учитывать возмож­
ность перехода naJDK 1.1 API для написания аплетов.

Основные сведения о Java


Платформа Java 2 предоставляет богатые возможности. В данной книге вы найдете
информацию, достаточную для того, чтобы начать разработку распределенных при­
ложений на Java.
В главах 7 и 8 приводятся общие сведения о синтаксисе Java. В главе 9 будут рас­
смотрены аплеты, кроме того, в главе 12 вы узнаете о диспетчерах компоновки, ис­
пользуемых при разработке графических пользовательских интерфейсов средствами
AWT (они описаны в главе 13) либо Swing (о них речь пойдет в главах 14 и 15).
После рассмотрения базового материала мы перейдем к обсуждению распреде­
ленных программ. Сначала речь пойдет о ТСР-соединениях, с помощью которых реа­
лизуется взаимодействие различных компьютеров в Internet (подробно об этом в гла­
ве 17). Для эффективной обработки клиентских запросов распределенные приложе­
ния часто реализуются в виде многопотоковых программ. Работе с потоками и
синхронизации данных (предотвращению проблем, возникающих при одновремен­
ном доступе к ресурсам) посвящена глава 16.
После этого мы перейдем к программам, выполняющимся на стороне сервера.
В глава 19 вы познакомитесь с Java-сервлетами, а в главе 20— с JavaServer Pages. Здесь
же будут рассмотрены cookie, поддержка сеансов и JavaBeans. Основные сведения о
взаимодействии с базами данных посредством JDBC вы получите в главе 22. Наконец,
в главе 23 мы затронем вопросы использования XML и JAXP для поддержки платфор-
менно-независимых бизнес-транзакций.
Усвоив указанный материал, вы сможете приступить к созданию распределенных
приложений на Java, а также изучать более сложные вопросы, включая Enterprise
JavaBeans, JNDI и JavaMessaging.

6.4. Начинаем работу


Итак, заканчиваем рассуждать и начинаем работать.
Если вы хотите, чтобы изучение Java продвигалось быстро, не стоит без остановки
читать эту книгу. <ШУТКА> Несомненно, она написана так интересно, что оторваться
от нее будет крайне сложно, но вам все же придется сделать это. </ШУТКА> Если гово­
рить серьезно, то лучше всего для вас будет как можно скорее инсталлировать Java и,
немного почитав, тут же проверить полученные знания на практике. По мере изуче­
ния материала примеры, реализованные вами, будут усложняться. Как можно быстрее
6.4. Начинаем работу 195

приступайте к написанию реальных программ и пробуйте применять при этом самые


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

Установка Java
Средства Java поставляются со многими операционными системами (например,
OS/2, MacOS 10, Solaris 2.6), поэтому не исключено, что Java уже инсталлирован на
вашем компьютере. Если это не так, вам доступны свободно распространяемые вер­
сии для Windows MacOS, OS/2, Novell IntranetWare, Solaris, Irix, HP-UX, AIX, SCO
UnixWare, Linux, Amiga, BeOS и многих других операционных систем. Ниже перечис­
лены адреса основных узлов, с которых можно скопировать различные версии Java.
Что касается других операционных систем и коммерческих продуктов, информацию
о них вы найдете на сервере Sun по адресу
http://Java.sun.com/cgi-bin/java-ports.cgi
Заметьте, что информация, расположенная по многим URL, приведенным в этой
книге, доступна также по адресу h t t p : //www. corewebprogramming. com/.

Java SDK, Standard Edition, Version 1.3 (JDK 1.3)


Microsoft Windows
http://Java.sun.com/j2se/l.3/download~windows.html

Solaris SPARC/x86
http://Java.sun.com/j2se/l.3/download-solaris.html

Linux x86
http://Java.sun.com/j2se/l.3/download-linux.html

Java SDK, Standard Edition, Version 1.2 (JDK 1.2)


Microsoft Windows
http://Java.sun.com/products/jdk/1.2/download-windows.html

Solaris SPARC/x86
http://Java.sun.com/products/jdk/1.2/download-solaris.html
196 Глава 6. Общие сведения о языке Java

L i n u x х86
http://Java.sun.com/products/jdk/1.2/download-linux.html

Java Development Kit, Version 1.1 (JDK 1.1)


Microsoft Windows
http://Java.sun.com/products/jdk/1.1/downlead-windows.html

Solaris SPARC/x86
http://Java.sun.com/products/jdk/1.2/download-jdk-solaris.html

Инсталляция броузера с поддержкой Java


Инсталлировав броузер с поддержкой Java, вы сможете запускать Java-программы,
встроенные в Web-страницы, т.е. аплеты. В состав интегрированных сред разработки и
свободно распространяемых версий Java входит утилита a p p l e t v i e w e r — своеобраз­
ный "мини-броузер", игнорирующий все компоненты Web-страницы за исключением
аплетов. Такую утилиту удобно использовать для тестирования аплетов. Для более тща­
тельной проверки можно использовать Netscape Navigator или Communicator, Microsoft
Internet Explorer, Hotjava производства Sun либо другой броузер. П р и этом возникает
"проблема первичности", так как для того, чтобы обратиться к Web-узлу и скопировать
броузер, надо иметь программу, поддерживающую протокол HTTP, т.е. броузер. Для
некоторых платформ броузер входит в состав операционной системы, часто их предос­
тавляют Internet-провайдеры. Кроме того, вы можете обратиться на FTP-узел Netscape.

N e t s c a p e Navigator
http://home.netscape.com/download/

Microsoft I n t e r n e t E x p l o r e r

http://www.microsoft.com/ie/download/

Hotjava
http://Java.sun.com/products/hotjava/

Доступ к документации по Java API


О ф и ц и а л ь н ы й док)т^1ент, описывающий Java API, предоставляет и н ф о р м а ц и ю обо
всех переменных (кроме переменных, объявленных как p r i v a t e ) и методах каждой
из стандартных библиотек. Таких исчерпывающих сведений вы не найдете ни в этой,
ни в какой-либо другой книге. HTML-версия документа для JDK 1.1, 1.2 и 1.3 распро­
страняется Sun и поставляется в составе многих интегрированных пакетов разработ­
ки. С документом можно работать в интерактивном режиме, обращаясь к Web-узлу
Sun, однако серьезные разработчики предпочитают копировать его на локальный
диск (для этого требуется 5-10 Мбайт дискового пространства).
6-4. Начинаем работу 197

Java 2 SDK, Version 1.3 (JDK 1.3)


Спецификация API
http://Java.sun.com/j2se/l.3/docs/api/

Узел, с которого можно скопировать документацию API


http://Java.sun.com/j2se/l.3/docs.html

Java 2 SDK, Version 1.2 (JDK 1.2)


Спецификация API
http://Java.sun.com/products/jdk/1.2/docs/api/

Узел, с которого можно скопировать документацию API


http://Java.sun.com/products/jdk/1.2/download-docs.html

Java 1.1 (JDK 1.1)


Спецификация API
http://Java.sun.com/products/jdk/1.1/docs/api/packages.html

Узел, с которого можно скопировать документацию API


http://Java.sun.com/products/jdk/1.l/#docs

Для того чтобы получить список всех доступных продуктов, надо обратиться к узлу
Sun по адресу h t t p : / / J a v a . s u n . c o m / p r o d u c t s / .

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


(необязательное действие)
Помимо Java-компилятора, вы, возможно, захотите установить интегрированный
пакет разработки, включающий графический отладчик, броузер классов, средства
создания интерфейсов, шаблоны для реализации соединений с базами данных и дру­
гими инструментами. В настоящее время доступны самые различные среды разработ­
ки, в частности, набор подобных пакетов расположен по адресу h t t p : / / J a v a ,
miningco . com/msub9 . htm. Ниже перечислено несколько дополнительных ссылок.

Borland JBuilder
http://www.boгland.com/jbuilder/

IBM VisualAge
http://www-4.ibm.com/software/ad/vajava/
198 Глава 6. Общие сведения о языке Java

Oracle J D e v e l o p e r

http://www.oracle.com/ip/develop/ids/jdeveloper.html

WebGain Visual Cafe

http://www.visualeafe.com/Products/VisualCafe_Overview.html

Sun Forte D e v e l o p e r
http://www.sun.com/forte/ffj/

Создание и запуск иауа-программы


Создание файла с исходным текстом
Создайте и сохраните файл (с именем T e s t . J a v a ) , содержащий общедоступный
класс T e s t . Заметьте, что имя файла и имя класса должны совпадать, кроме того, в
этих именах учитывается регистр символов. Если вы не используете окружение,
специально предназначенное для разработки, создайте файл с исходным текстом с
помощью обычного текстового редактора. П р и м е р исходного кода программы
приведен в следующем разделе.

Компиляция
Если вы работаете в системе Windows или UNIX и используете компилятор j a v a c
из пакета Sun JDK, скомпилируйте текст, выполнив команду j a v a c T e s t . J a v a .
В системе Macintosh перетащите исходный файл на Java-компилятор. Если вы ра­
ботаете в и н т е г р и р о в а н н о й среде, следуйте инструкциям поставщика. В результа­
те компиляции создается файл T e s t , c l a s s .

Запуск п р о г р а м м ы
Для запуска независимого Java-приложения с консольным и н т е р ф е й с о м можно
вызвать команду J a v a T e s t . Заметьте, что утилита называется не j a v a c , а J a v a ,
кроме того, в качестве параметра задается T e s t , а не T e s t . c l a s s . В Mac доста­
точно перетащить файл класса на программу запуска. Для выполнения аплета вам
потребуется броузер, в котором необходимо открыть Web-страницу, содержащую
аплет. Так, например, чтобы запустить аплет в составе Web-страницы T e s t . h t m l ,
эта страница должна ссылаться на файл T e s t , c l a s s посредством дескриптора
<APPLET>. Подробно о запуске аплетов мы поговорим позже.

6.5. Несколько простых Java-программ


В этом разделе представлено несколько очень простых программ, посредством
которых состоится ваше знакомство с Java. Если вы не совсем понимаете некоторые
детали — не стоит беспокоиться; в дальнейшем мы подробно рассмотрим структуру
типичной программы на Java. Ж е л а т е л ь н о скомпилировать эти программы и запус­
тить их на выполнение. После того как они выполнятся успешно, постарайтесь вне­
сти в них хотя бы минимальные изменения.
6 . 5 . Несколько простых J a v a - п р о г р а м м 199

Приложение Hello, World


Java-приложениями (в отличие от Java-аплетов) принято называть независимые
Java-программы. Приложение должно содержать имя, которое в точности (с учетом
регистра символов) совпадает с именем исходного файла. Этому классу должен при­
надлежать метод main, определенный как p u b l i c s t a t i c void. При вызове метода
main ему передается строковый массив, определяемый как S t r i n g [ ] имя_параметра
либо как S t r i n g гшя__параметра[ ]. В листинге 6.1 представлен код простого прило­
жения, которое выводит после запуска сообщение "Hello, world". Другие примеры
приложений приведены в главе 7. Часто Java-приложения обеспечивают графический
пользовательский интерфейс. Общие сведения о графических интерфейсах будут
представлены в главе 9, а более подробно они будут рассмотрены в главе 13, посвя­
щенной компонентам AWT.
М е т о д и к а профессионалов

Исходный код общедоступного класса SomeClass должен содер­


жаться в файле SomeClass. Java. Поскольку в системе Windows
98/NT/2000 учитывается регистр символов, имя файла не может
быть SOMECLASS. Java ИЛИ someclass. Java.

Л и с т и н г 6 . 1 . HeiioWorid.Java

public class HelloWorld {


public static void main(String[] args) {
System.out.println{"Hello, world.");
}
}

Компиляция:
javac HelloWorld.Java
Запуск:
Java HelloWorld
Результаты выполнения:
H e l l o , world.

Параметры командной строки


в листинге 6.3 показан код программы, которая реагирует на данные, введенные
пользователем. Данный пример выглядит почти так же, как программа, написанная
на языке С, однако имеет несколько важных отличий. Объект S t r i n g является ча­
стью Java, с каждым из Java-массивов связано свойство l e n g t h , а имя файла не входит
в состав параметров командной строки. Если вы никогда не видели программы на С
или C++, вам следует ознакомиться с циклами и условными операторами, сведения о
которых приведены в главе 8. Заметьте, что в Macintosh вы также можете читать па-
200 Глава 6. Общие сведения о языке Java

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

Листинг 6 . 2 . S h o w A r g s . J a v a

p u b l i c c l a s s ShowArgs {
public s t a t i c void main(String[] args) {
f o r ( i n t i = 0 ; i < a r g s . l e n g t h ; i++) {
System.out.println("Arg " + i + " is " + args[i];
}
}
}

Компиляция:
javac ShowArgs.Java
Запуск:
Java ShowArgs fee fie foe fum
Результаты выполнения:
Arg 0 is fee
Arg 1 is fie
Arg 2 is foe
Arg 3 is fum

Аплет Hello, World


Аплет отличается от приложения тем, что он входит в состав Web-страницы и вы­
полняется в среде, реализуемой броузером. Подобно приложению, в исходном файле
аплета определен класс, имя которого совпадает с именем файла, однако данный
класс не содержит метод m a i n . Инициализация аплета обычно осуществляется в теле
метода i n i t , а вывод данных— в теле метода p a i n t . В листинге 6.3 содержится код
простого Java-аплета, который выводит в небольшом окне сообщение "Hello, World
Wide Web". В листинге 6.4 показан HTML-документ, включающий данный аплет. За­
метьте, что имя HTML-файла может быть произвольным; оно не обязательно должно
совпадать с именем исходного Java-файла. В данном случае имена выбраны совпадаю­
щими исключительно для удобства рассмотрения примера. Дополнительную инфор­
мацию об аплетах и выводе данных в окнах вы найдете в главе 9.

Листинг 6 . 3 . HelloWWW. J a v a

import Java.applet.Applet;
import java.awt.*;

p u b l i c c l a s s HelloWWW e x t e n d s A p p l e t {
p r i v a t e i n t f o n t S i z e = 40;
6.5. Несколько простых Java-программ 201

public void initO {


setBackground(Color.black);
setForeground(Color.white);
setFont(new Font("SansSerif", Font.BOLD, fontSize));

public void paint(Graphics g) {


g.drawstring("Hello, World Wide Web.", 5, fontSize+5);

Лиcтинг 6.4. HelloWWW.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>HelloWWW: Simple Applet Test.</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">

<Hl>HelloWWW: Simple Applet Test.</Hl>


<P>
<APPLET CODE="HelloWWW.class" WIDTH=460 HEIGHT=50>
<B>Error! You must use a Java enabled browser.</B>
</APPLET>

</BODY>
</HTML>

Компиляция:
javac HelloWWW.Java
Запуск:
HelloWWW. h t m l загружается в Web-броузер.

Результаты выполнения:
Т и п и ч н ы е результаты показаны на рис. 6.6.

F^ HelloWWW: Simple Aopiet Tesl.


£ile £(A» Yiew Qo Communcatc»
Nfstscape
ЦФ
ШШ
a
HelloWWW: Simple Applet Test.

Hello. World Wide Web.


Ci?''^'<' App{(s{netoWV,Vf<^Д11^Ш^|рШ!|р.Р га Z
Рис. 6.6. Простой аплет, отображаемый в окне бро­
узера Netscape Navigator 4.7, который выполняется в
среде Windows 98
202 Глава 6. Общие сведения о языке Java

Параметры, передаваемые аплету


Аплет не получает параметры из командной строки по той причине, что он запус­
кается в среде броузера. Для того чтобы передать параметры аплету, надо использо­
вать элемент PARAM, помещаемый между дескрипторами <APPLET . . .> и
</APPLET>. Аплет читает параметры, вызывая метод g e t P a r a m e t e r . В листинге 6.5
представлен код модифицированного аплета HelloWWW, который отображает текст,
переданный ему в качестве параметра. В листинге 6.6 показан HTML-документ, кото­
рый четырежды вызывает аплет, передавая ему различные параметры. Подробно ис­
пользование элемента PARAM будет рассмотрено в главе 9.

Листинг 6.5. Message.java

import Java . a p p l e t . Applets-


import j a v a . a w t . * ;
p u b l i c c l a s s Message extends Applet {
private int fontSize;
p r i v a t e S t r i n g messages-
p u b l i c void i n i t O {
setBackground(Color.black);
setForeground(Color.white);
/ / Установка размера шрифта на базе области,
/ / выделяемой для аплета.
f o n t S i z e = g e t S i z e О . h e i g h t - 10;
setFont(new F o n t ( " S a n s S e r i f " , Font.BOLD, f o n t S i z e ) ) ;
/ / Чтение сообщения, переданного посредством
/ / HTML-элемента PARAM.
message = getParameter("MESSAGE");
}
p u b l i c void p a i n t ( G r a p h i c s g) {
i f (message != n u l l ) {
g . d r a w s t r i n g ( m e s s a g e , 5, f o n t S i z e + 5 ) ;
}
}

Листинг 6.6. Message.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>The Message Applet</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>The <CODE>Message</CODE> Applet</Hl>
<P>
<APPLET CODE="Message.class" WIDTH=325 HEIGHT=25>
<PARAM NAME="MESSAGE" VALUE="Tiny">
6.5. Несколько простых Java-программ 203

<B>Sorry, these exampl es require Java</B>


</APPLET>
<P>
<APPLET CODE="Message.cl ass" WIDTH=325 HEIGHT=50>
<PARAM NAME-"MESSAGE" VALUE="Small">
<B>Sorry, these exampl es require Java</B>
</APPLET>
<P>
<APPLET CODE="Message.cl ass" WIDTH=325 HEIGHT=75>
<PARAM NAME="MESSAGE VALUE="Medium">
<B>Sorry, these exampl es require Java</B>
</APPLET>
<P>
<APPLET CODE="Message.cl ass" WIDTH=325 HEIGHT=100>
<PARAM NAME="MESSAGE" VALUE="Giant">
<B>Sorry, these exampl es require Java</B>
</APPLET>
</BODY>
</HTML>

Компиляция:
javac Message.Java
Запуск:
M e s s a g e . h t m l загружается в Web-броузер.
Результаты выполнения:
На рис. 6.7 показана Web-страница, в которой один и тот же аплет загружается че­
тыре раза. П р и вызове аплета в составе дескриптора APPLET задаются разные значе­
ния атрибута HEIGHT, а в дескрипторе PARAM — разные значения атрибута VALUE.

JSIxJ
, Ete 1 * iiew Favorites lools Неч

>i-iBack ' ->- - . J J 'JU 'i^Search

d
T h e Message A p p l e t

Small

Medium
Giant
i^jOone ;уЗ My Computer
J
Рис. 6.7. Один и тот же аплет четырежды
отображается в окне броузера Explorer 5.5,
который выполняется в системе Windows 2000
204 Глава 6. Общие сведения о языке Java

6.6. Резюме
Язык Java был впервые представлен несколько лет назад и бурно развивался все
эти годы. Большинство разработчиков не встречались прежде с н е к о т о р ы м и средст­
вами Java, тем не менее, идеи, на базе которых созданы эти средства, уже были неод­
нократно реализованы в других языках. Целью данной главы было предоставить чи­
тателю общие сведения о Java и развеять л о ж н ы е представления о данном языке. По­
сле инсталляции Java, броузера с поддержкой Java и документации по Java API можно
приступать к созданию первых программ.
Дальнейшие ваши действия зависят от того, насколько богат ваш опыт программи­
рования. Если вы никогда не работали с объектно-ориентированными языками, вам
следует внимательно прочитать главу 7. Если же объектно-ориентированный подход —
не новость для вас, то достаточно бегло просмотреть эту главу для того, чтобы узнать о
некоторых особенностях, отличающих Java от других объектно-ориентированных язы­
ков. Аналогично, если вы ничего или почти ничего не знаете о С или C++, вам надо вни­
мательно прочитать главу 8, где рассматривается синтаксис Java, и попытаться написать
несколько простых программ. Если же ваш опыт программирования на C/C++ доста­
точно велик, вам следует лишь пролистать главу 8 и перейти к более сложному материа-
лу. В последующих главах будут рассмотрены работа с окнами и графикой, поддержка
событий, многопотоковая обработка, создание программ, взаимодействующих по сети,
Java-сервлеты, JavaServer Pages и прочие вопросы.
ОБЪЕКТНО-
ОРИЕНТИРОВАННОЕ
ПРОГРАММИРО­
ВАНИЕ НА JAVA

В ЭТОЙ главе...

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


с именованными полями.
Методы: добавление функций к классам.
Конструкторы: функции, используемые при построении
классов.
Деструкторы: почему в Java не используются функции,
предназначенные для освобождения ресурсов.
Javadoc: создание гипертекстовой документации для классов.
Наследование: повторное использование классов
и обеспечение новых возможностей.
Интерфейсы: описание поведения различных классов.
Пакеты: организация классов.
CLASSPATH: поиск файлов классов.
Модификаторы: управление доступом к различным частям
классов.
Tly\:EJ^zJ

О сновным понятием при программировании на языке Java являются объекты.


Начинающем)' программисту, работающем)' на Java, необходимо разобраться,
как создаются и используются объекты. П о н я т ь п р и н ц и п ы работы с объекта­
ми важнее даже, чем изучить основные синтаксические конструкции, описанные в
главе 8. Если вы никогда на занимались объектно-ориентированным программирова­
нием, уделите особое внимание материалу, изложенному в этой главе. Как уже было
сказано, выполнять п р и м е р ы не менее важно, чем читать текст главы. В процессе
изучения материала постарайтесь создать несколько собственных классов. Время, по­
траченное на работу с примерами, многократно окупится при решении конкретных
задач. Если вы работали с объектами на других языках программирования, данная
глава вряд ли будет вам полезна; со многими разделами вам достаточно лишь бегло
ознакомиться. Однако в любом случае необходимо уделить пристальное внимание
разделу 7.7, посвященному Javadoc, разделу 7.9, в котором описываются и н т е р ф е й с ы
и абстрактные классы, а также разделу 7.10, в котором рассматриваются пакеты, пе­
ременная окружения CLASSPATH и архивы JAR.

7 . 1 . Переменные экземпляра
Класс можно упрощенно представить себе как структуру или запись. О б ы ч н о для
создания объекта (экземпляра класса) используется о п е р а т о р new, после которого
указывается конструктор класса. Конструктор — это метод, имя которого совпадает с
именем класса. Н а п р и м е р :
Point р1 = new Point(2, 4);
Color red = new Color(255, 0, 0);
В некоторых случаях использование оператора new остается скрытым от разра­
ботчика. Н а п р и м е р , объект может возвращаться в результате выполнения метода, в
этом случае о п е р а т о р new выполняется в теле метода, либо метод вовсе не обращает­
ся к данному оператору, а возвращает существующий экземпляр класса. П р и м е р ы
объектов, возвращаемых методами, приведены ниже.
208 Глава 7. Объектно-ориентированное программирование.

OutputStream out = someSocket.getOutputStream();


Point pi = someWindow.location 0 ;
В некоторых случаях, например при работе со строками и массивами, для созда­
ния объектов используются упрощенные конструкции. Например, ниже показаны два
способа создания объекта S t r i n g .
String stringl = new String("A String");
String string2 = "Another String";
Поля, или свойства, класса в Java принято называть переменными экземпляра. Для об­
ращения к ним используются выражения, подобные следующему:
objectReference.variableName
Как видите, имя переменной экземпляра отделяется точкой от ссылки на реаль­
ный объект.
В листинге 7.1 показан пример класса с именем S h i p l (он может представлять
судно в некоторой системе моделирования). В теле метода m a i n посредством выра­
жений new S h i p l () создаются два экземпляра класса S h i p l , а затем инициализиру­
ются переменные каждого объекта. Значения переменных изменяются, моделируя
"движение" судна, после чего скорость и координаты судов выводятся на экран.

Листинг 7 . 1 . T e s t l . j a v a

// Создание к л а с с а с пятью переменными э к з е м п л я р а ( п о л я м и ) :


// X, у, s p e e d , d i r e c t i o n и name. З а м е т ь т е , ч т о к л а с с S h i p l
// не о б ъ я в л е н к а к p u b l i c , поэтому он может быть о п р е д е л е н
// в том же файле, ч т о и T e s t l . В J a v a - ф а й л е может быть
// о п р е д е л е н т о л ь к о один общедоступный ( p u b l i c ) к л а с с .

class Shipl {
public double x, у, speed, direction;
public String name;
}

// Класс, предназначенный для запуска программы,


// содержащий метод main.

public class Testl {


public static void main(String[] args) {
Shipl si = new Shipl();
s1.X = 0.0;
si.у = 0.0;
si.speed = 1.0;
si.direction = 0.0; // Восток
si.name = "Shipl";
Shipl s2 = new Shipl();
s2.x = 0.0;
s2.y = 0.0;
s2.speed = 2.0;
s2.direction = 135.0; // Северо-запад
s2.name = "Ship2";
sl.x = sl.x + si.speed
* Math.cos(si.direction * Math.PI / 180.0);
7 . 1 . Переменные экземпляра 209

si.у = si.у + si.speed


* Math.sin(sl.direction * Math.PI / 180.0)
s2.x = s2.x + s2.speed
'^ Math, cos (s2.direction * Math.PI / 180.0)
s2.y = s2.y + s2,speed
* Math.sin(s2.direction * Math.PI / 180.0)
System.out.println(si.name + " is at ("
+ si.X + "," + si.y + ").");
System.out.println(s2.name + " is at ("
+ s2.x + "," + s2.y + ").");
}

Компиляция и запуск:
javac T e s t l . j a v a
Java T e s t l
Результаты выполнения:
Shipl is at (1,0).
Ship2 is at (-1.41421,1.41421).
П р о с м о т р е в текст данного примера, вы, возможно, заметили в именах перемен­
ных и классов н е к о т о р ы е особенности. В соответствии со стандартными соглаше­
ниями об именовании Java имена локальных переменных и переменных экземпляра
начинаются со с т р о ч н о й буквы (например, s o m e S t r i n g , window, o u t p u t S t r e a m l ) , а
имена классов начинаются с прописной буквы (например. S t r i n g , Window,
O u t p u t S t r e a m ) . Если имя переменной или класса состоит из нескольких "слов", каж­
дое из последующих "слов" п р и н я т о начинать с прописной буквы (например,
s o m e l n s t a n c e V a r i a b l e , S o m e C l a s s ) . Н е к о т о р ы е разработчики предпочитают раз­
делять "слова" знаками подчеркивания (например, someinstancevariable,
S o m e C l a s s ) . Имена констант состоят из прописных букв (например, P I ) . Данное
соглашение делает код более удобочитаемым, по имени можно сразу сказать, является
ли оно именем класса или переменной.

Методика профессионалов
Имя переменной начинается со строчной буквы (myVar), а имя клас­
са—с прописной (myCLass).

На первый взгляд может показаться, что в тексте, представленном в листинге 7.1,


не соблюдаются соглашения об именовании. Действительно, скажет читатель, если
Math— это глобальная переменная, содержащая объект с константой P I , т о ее имя
должно начинаться со строчной буквы. Почему же тогда в тексте примера указано
Math вместо m a t h ? Дело в том, что Math — не глобальная переменная (в языке Java во­
все не существует такого понятия), а класс. Помимо переменных экземпляра в Java
определены переменные iciacca, или статические переменные, которые совместно исполь­
зуются всеми экземплярами одного и того же класса. П р и объявлении таких перемен­
ных задается ключевое слово s t a t i c , и для обращения к ним достаточно указать имя
класса. Таким образом, соглашение об именовании позволяет разобраться с реальным
210 Глава 7. Объектно-ориентированное программирование..

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


нии класса Math, глядя на выражение M a t h . P I , может сказать, что р е ч ь идет о пере­
менной s t a t i c f i n a l (константе) P I , которая является переменной класса Math.

7.2. Методы
В предыдущем примере один и тот же фрагмент кода повторялся несколько раз
для того, чтобы присвоить значения переменным экземпляра х и у двух объектов. Та­
кой стиль программирования считается плохим не только потом)% что одинаковые
фрагменты повторяются многократно, сколько потому, что при модификации про­
граммы надо вносить изменения сразу в нескольких ее частях. Для того чтобы решить
эт)^ проблему, включим функцию в состав класса, который до сих п о р содержал только
данные. Код такого класса показан в листинге 7.2. Функции, входящие в состав класса,
п р и н я т о называть, как и в L i s p / C L O S , методами (в языке C++ они называются функ­
циями-членами). Заметьте, что в отличие от C++, в Java переменные экземпляра могут
инициализироваться при объявлении.
public double х=0.0;
П р и объявлении переменных и методов могут использоваться ключевые слова
p u b l i c и p r i v a t e , которые п р и н я т о называть модификаторами. П о д р о б н о они буд)т
описаны ниже в этой главе, сейчас же достаточно заметить, что м о д и ф и к а т о р p u b l i c
разрешает дост)'п к переменной или методу из других объектов. Модификатор p r i ­
v a t e , напротив, ограничивает доступ рамками данного класса.

Листинг 7.2. T e s t 2 . j a v a

/ / К к л а с с у S h i p д о б а в л я ю т с я методы move и p r i n t L o c a t i o n ,
/ / объявленные к а к p u b l i c

class Ship2 {
public double x=0.0, y=0.0, speed=1.0, direction=0.0;
public String name = "UnnamedShip";

private double degreesToRadians(double degrees) {


return(degrees * Math.PI / 180.0);
}
public void move() {
double angle = degreesToRadians(direction);
X = X + speed * Math.cos(angle);
у = у + speed * Math.sin(angle);
}
public void printLocation() {
System.out.println(name + " is at " +
" (" + X + ", " 4- у + " ) . " ) ;
}
}
public class Test2 {
7 . 3 . Конструкторы и ссылка this 211

public static void main(String[] args) {


Ship2 si = new Ship2();
si.name = "Shipl";
Ship2 s2 = new Ship2();
s2.direction = 135.0; // Северо-запад
s2.speed = 2.0;
s2.name = "Ship2";
s1.move();
s2.move();
si.printLocation();
s2.printLocation();
}

Компиляция и запуск:
javac Test2.java
Java Test2
Результаты выполнения:
Shipl is at (1, 0) .
Ship2 is at (-1.41421,1-41421).

7.3. Конструкторы и ссылка this


Конструктор класса — специальная процедура, используемая для создания объекта.
Конструктор указывается в вырг1жении типа new C l a s s N a m e ( . . . ) . По сути, конст­
руктор представляет собой общедоступный метод, на который накладываются два ог­
раничения. Во-первых, имя конструктора должно совпадать с именем класса, а во-
вторых, при объявлении конструктора не должен указываться возвращаемый тип. Ес­
ли возвращаемый тип будет указан (например, p u b l i c v o i d S h i p 2 ( . . . ) {...}),
то при компиляции не выводятся предупреждающие сообщения, но объявленный ме­
тод не будет вызываться как конструктор.

Внимание!

Убедитесь, что при объявлении конструктора не указан тип возвра­


щаемого значения.

П р и определении конструктора можно указать любое количество параметров. Па-


р а м е т р ы п о з в о л я ю т разработчику передавать данные для инициализации объекта в
процессе его создания. Если вы не определите ни одного конструктора, Java автома­
тически создаст конструктор, вызываемый без параметров, в теле которого не содер­
жится ни одного оператора.
p u b l i c SomeClassO { }
Если же вы определите любой конструктор для класса, конструктор по умолчанию
создаваться не будет. Таким образом, если вы задали конструктор, которому переда­
ется один или несколько параметров, но хотите, чтобы конструктор без параметров
также существовал, вам надо определить его явным образом.
212 Глава 7 . Объектно-ориентированное п р о г р а м м и р о в а н и е . . .

На з а м е т к у

Если вы не определили ни одного конструктора для класса, Java ав­


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

Недостаток класса S h i p 2 состоит в том, что изменение полей происходит в не­


сколько этапов. Удобнее было бы задавать их значения при создании объекта. Более
того, наличие значений по умолчанию затрудняет чтение текста программы, по­
скольку глядя на команду создания объекта, приходится учитывать величины, автома­
тически присваиваемые переменным. Ч т о б ы избежать этого недостатка, создадим
класс S h i p 3 , конструктор которого выглядит следующим образом:
public Ship3(double х, double у, ...) {
// Инициализация полей
}
Однако здесь также возникает небольшая проблема: локальные п е р е м е н н ы е х и у
маскируют переменные экземпляра с теми же именами. Фрагмент кода, показанный
ниже, синтаксически корректен, но абсолютно бесполезен.
public Ship3(double х, double у, ...) {
X = х;
У = у;

}
Единственным результатом выполнения этого кода будет п о в т о р н о е присвоение
локальным переменным тех же значений. Один из выходов из создавшегося положе­
ния — использовать различные имена для локальных переменных и переменных эк­
земпляра.
public Ship3(double inputX, double inputY, ...) {
X = inputX;
у = inputY;

Другое решение проблемы состоит в использовании переменной t h i s . В любом


классе переменная t h i s ссылается на текущий экземпляр класса. Эта переменная мо­
жет быть, в частности, использована для передачи внешним программам ссылки на
объект. С помощью переменной t h i s можно также ссылаться на переменные экземп­
ляра и методы, как это показано ниже.
public void move() {
double angle = this.degreesToRadians(this.direction);
this.x = this.x + this.speed * Math.cos(this.angle);
this.у = this.у + this.speed * Math.sin(this.angle);
}
Этот фрагмент абсолютно идентичен следующему, записанному более просто:
public void move() {
double angle = degreesToRadians(direction);
7.3. Конструкторы и ссылка this 213

X = X + speed * Math.cos(angle);
у = у + speed * Math.sin(angle);
}
Указывать переменную t h i s перед именем каждой переменной экземпляра или
метода нет никакой необходимости, однако в некоторых случаях данная переменная
оказывается полезной. Так, например, с ее помощью можно отделить локальные пе­
ременные от полей класса с теми же именами.

Листинг 7 . 3 . T e s t 3 . J a v a

/ / В к о н с т р у к т о р е S h i p 3 при с о з д а н и и о б ъ е к т а происходит
/ / и н и ц и а л и з а ц и я переменных э к з е м п л я р а .

class Ship3 {
public double x, у, speed, directions-
public String name;
public Ship3(double x, double y, double speed,
double direction, String name) {
this.x = x; // Переменная "this" позволяет отделить
this.у = у; // переменные экземпляра от локальных переменных,
this.speed = speed;
this.direction = direction;
this.name = name;

private double degreesToRadians(double degrees)


return(degrees * Math.PI / 180.0);

public void move() {


double angle = degreesToRadians(direction)
X = X + speed * Math.cos(angle);
у = у + speed * Math.sin(angle);

public void printLocation() {


System.out.println(name + " is at " +
" (" + X + ", " + у + ") . ")

public class Tests {


public static void main(String[] args) {
Ships si = new ShipS(0.0, 0.0, 1.0, 0.0, "Shipl")
Ships s2 = new ShipS(0.0, 0.0, 2.0, 135.0, "Ship2")
s1.move();
s 2.move();
si.printLocation();
s2.printLocation();
214 Глава 7. Объектно-ориентированное программирование.

Компиляция и запуск:
javac Test3.java
Java T e s t s
Результаты выполнения:
Shipl is at (1,0).
Ship2 is at (-1.41421,1.41421).

Статические инициализационные блоки


Если вам необходима более сложная языковая конструкция, чем значения, при­
сваиваемые по )ти[олчанию, но более простая, чем конструкторы, вы можете приме­
нить статический инициализационный блок, который помечается модификатором
s t a t i c и выполняется при загрузке класса. Класс загружается при создании первого
его экземпляра, при первом обращении к переменной класса либо при первом вызове
метода класса. П р и м е р статического блока приведен ниже.
public class SomeClass {
int[] values = new int[12];

static {
for (int i=0; i<values . length; i-»-+) {
values[i] = 2 * i + 5;
}
}

int lastValue = values[11];

}
В большинстве случаев действия по инициализации выполняются в теле конструк­
тора. Статические инициализационные блоки применяются крайне редко.

7.4. Деструкторы
Д а н н ы й раздел вполне мог бы отсутствовать.
Дело в том, что в Java нет необходимости в использовании деструкторов
(функций, предназначенных для разрушения объектов). После удаления последней
ссылки на объект система "сборки мусора" освобождает память, занимаемую этим
объектом. Если ссылка на объект содержится в локальной переменной, объект удаля­
ется после завершения работы метода либо раньше, при изменении значения пере­
менной. Если на объект ссылается переменная экземпляра, то объект удаляется после
присвоения этой переменной нового значения. Следует признать, что деструкторы —
не самая удобная конструкция, используемая в языке С. В Java деструкторов нет, но
программы продолжают работать нормально. В Java не может быть указателей, ссы­
лающихся на неиспользуемые области памяти. Пока переменная ссылается на объект,
этот объект продолжает существовать. В Java не могут оставаться неиспользуемые об­
ласти памяти: система "сборки мусора" освобождает память, занимаемую объектами,
7-5- Переопределение 215

ссылки на которые отсутствуют. Данная система также выявляет "кольцевые ссылки",


когда объекты ссылаются друг на друга, по не существует ни одной ссылки вне преде­
лов некоторого набора. При разработке программ на Java остается лишь проблема
"забытых" объектов, ссылки на которые существуют, но не используются, однако этот
вопрос стоит не так остро, как проблемы, характерные для языка C++.
Несмотря на операцию "сборки мусора", при разработке программ может возник­
нуть необходимость предусмотреть выполнение некоторых действий перед удалени­
ем объекта. В такой ситуации может использовать метод f i n a l i z e объекта.
p r o t e c t e d void f i n a l i z e ( ) throws Throwable {
doSomeBookkeeping();
s u p e r . f i n a l i z e { ) ; / / Использование метода f i n a l i z e r
/ / родительского класса.
}

На данном этапе вам не стоит задумываться о том, что означает выражение


throws и откуда Java знает о наличии метода f i n a l i z e . Эти вопросы будут подробно
рассмотрены несколько позже. Сейчас же вам достаточно знать, что если вы включи­
те в программу приведенный выше код, то в теле метода doSomeBookkeeping можно
выполнять необходимые действия.

7.5. Переопределение
Подобно C++ и другим объектно-ориентированным языкам, Java допускает сущест­
вование нескольких методов с одинаковыми именами. Эти методы различаются по
числу и типу передаваемых им параметров. Например, вы можете определить два ме­
тода i s B i g : один из них выявляет "большие", согласно выбранному вами критерию,
объекты S t r i n g , а другой определяет, превышает ли значение целочисленной пере­
менной некоторую заранее заданную величину.
public boolean i s B i g ( S t r i n g s) {
r e t u r n ( s . l e n g t h 0 > 10);
}

public boolean i s B i g ( i n t n) {
r e t u r n ( n > 1000);
}

Заметьте, что выражение


r e t u r n ( n > 1000);
выполняет те же действия, что и следующий фрагмент программы:
if (п > 1000) {
return(true);
} else {
return(false);
}

В листрп1ге 7.4 переопределяются конструктор Ship4 и метод move. Один конст­


руктор вызывает другой с помощью выражения t h i s ( a r g s ) . Этот вызов должен
осуществляться в первой строке конструктора. Не следует путать вызов конструктора
с переменной t h i s .
216 Глава 7. Объектно-ориентированное программирование.

public class SomeClass {


public SomeClassО {
this(12); // Вызов другого конструктора
doSomething();
}
public SomeClass(int num) {
doSomethingWith(num);
doSomeOtherStuff();
}

Вновь объявленный вариант метода move позволяет задавать число "шагов" при
перемещении судна. Определяя новый метод, вы можете создать в его тело вызов
старого метода, а можете построить программу так, что два метода будут выполняться
независимо друг от друга. В следующем примере при выполнении второго из указан­
ных методов вызывается первый метод move:
p u b l i c void move() {
double angle = d e g r e e s T o R a d i a n s ( d i r e c t i o n ) ;
X = X + speed * M a t h . c o s ( a n g l e ) ;
у = у 4- speed * Math, s i n (angle) ;
}

p u b l i c void move(int s t e p s ) {
for ( i n t 1=0; K s t e p s ; i++) {
move();
}
}

Код метода move ( i n t s t e p s ) можно переписать так, чтобы он выполнялся неза­


висимо от метода move ().
p u b l i c void move() {
double angle = d e g r e e s T o R a d i a n s ( d i r e c t i o n ) ;
X = X + speed * M a t h . c o s ( a n g l e ) ;
у = у + speed * M a t h . s i n ( a n g l e ) ;
}

p u b l i c void move2(int s t e p s ) {
double angle = d e g r e e s T o R a d i a n s ( d i r e c t i o n ) ;
X = X + ( d o u b l e ) s t e p s * speed * M a t h . c o s ( a n g l e ) ;
у = у + ( d o u b l e ) s t e p s * speed * M a t h . s i n ( a n g l e ) ;
}

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


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

p u b l i c void move{) {
move(1);

p u b l i c void move{int s t e p s ) {
double angle = d e g r e e s T o R a d i a n s ( d i r e c t i o n ) ;
X = X + ( d o u b l e ) s t e p s * speed * M a t h . c o s ( a n g l e ) ;
у = у + ( d o u b l e ) s t e p s * speed * M a t h . s i n ( a n g l e ) ;
}

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

Листинг 7 . 4 . T e s t 4 , j a v a

class Ship4 {
public double x=0.0, y=0.0, speed=1.0, direction=0.0;
public String name;

// При вызове данному конструктору передаются параметры.

public Ship4(double х, double у, double speed,


double direction, String name) {
this.x = x;
this.у = у;
this.speed = speed;
this.direction = direction;
this.name = name;

// Данному конструктору требуется параметр name, а значения


// X, у, speed и direction принимаются по умолчанию.

public Ship4(String name) {


this.name = name;

private double degreesToRadians(double degrees) {


return(degrees * Math.PI / 180.0);

// Перемещение на один шаг.

public void move() {


move(1);

// Перемещение на N шагов.

public void move(int steps) {


double angle = degreesToRadians(direction);
X = X + (double)steps * speed * Math.cos(angle);
218 Глава 7. Объектно-ориентированное программирование...

у = у + (double)steps * speed * Math.sin(angle);


}

public void printLocation() {


System.out.println(name + " is at (" + x + "," + у + " ) . " ) ;
}
}

public class Test4 {


public static void main(String[] args) {
Ship4 si = new Ship4("Shipl");
Ship4 s2 = new Ship4(0.0, 0.0, 2.0, 135.0, "Ship2")
s1.move();
s2.move(3);
si.printLocation();
s2.printLocation();
}
}

Компиляция и запуск:
javac Test4.java
Java Test4
Результаты выполнения:
Shipl is at (1,0).
Ship2 is at (-4.24264,4.24264).

7.6. Подготовка классов к повторному


использованию
Классы, предназначенные для р е ш е н и я одной задачи, обычно определяются в од­
ном файле, как это было сделано в предыдущих примерах. Иногда при разработке
классов учитывается возможность их повторного использования. Н а п р и м е р , в лис­
тинге 7.5 представлен код класса S h i p , а в листинге 7.6— программа, предназначен­
ная для тестирования класса. Для создания повторно используемого класса надо при­
ложить гораздо больше усилий, чем обычно, в частности, необходимо добиться рас­
ширяемости кода и снабдить его документацией. П р и этом желательно использовать
описанные ниже подходы.

Заменять переменные public специальными методами доступа


Вместо непосредственного чтения и записи данных для обращения к переменным
рекомендуется использовать специальные методы, позволяющие читать и записы­
вать значения переменных. Так, например, в предыдущем примере лучше объя­
вить переменные х и у как p r i v a t e и определить методы g e t X , g e t Y , s e t X и
s e t Y (методы доступа). Несмотря на то что такой подход предполагает дополни­
тельный объем работы по созданию программ, если класс будет широко использо­
ваться, затраты впоследствии окупятся.
7.6. Подготовка классов к повторному использованию 219

Прежде всего, методы дост)^па предоставляют возможность для дальнейшей мо­


дификации класса. Предположим, например, что разработчик решил реализовать
проверку на отсутствие ошибок и собирается следить за тем, чтобы значение на­
правления не было отрицательным, либо за тем, чтобы не превышалась макси­
мально допустимая скорость судна. Если другие объекты могут непосредственно
обращаться к переменным, реализация указанных выше проверок означает моди­
ф и к а ц и ю кода всех объектов, взаимодействующих с экземплярами данного класса.
Если же для установки значений переменных применяются методы s e t X и s e t Y ,
проверка реализуется без изменения других объектов. Предположим также, что
объект S h i p является частью сложной системы моделирования и каждое измене­
ние переменных х и у должно графически отображаться на экране. Методы s e t X
и s e t Y — наилучшее место для реализации соответствующего кода.
Использование методов доступа также защищает пользователей класса от измене­
ний его структуры. Предположим, например, что разработчик решил отказаться
от хранения координат в отдельных переменных х и у и использовать для этой
цели класс P o i n t . Если из других объектов производится непосредственное обра­
щение к переменным х и у, код этих объектов должен быть изменен. Если же для
чтения и записи значений переменных применяются методы дос1упа, изменению
подлежат лишь методы g e t X и g e t Y .

Использовать javadoc для создания электронной документации


Комментарии, находящиеся между последовательностями символов " / * * " и " * / " ,
используются программой j a v a d o c для создания гипертекстовых описаний всех
методов и переменных, в объявлении которых не было использовано ключевое
слово p r i v a t e . Утилита j a v a d o c будет рассмотрена в разделе 7.7. Как и раньше,
данный подход предполагает затрату дополнительных усилий по док)ТУ1ентирова-
нию программ, но при интенсивном использовании классов затраты окупаются.
Документ, представленный на рис. 7.1, непосредственно сгенерирован по коммен­
тариям, которыми снабжен код класса S h i p . j a v a (листинг 7.5).

Листинг 7 . 5 . S h i p . j a v a

/** Пример, демонстрирующий с о з д а н и е объектно-ориентированных


* программ на J a v a .

* @author <А H R E F = " m a i l t o : l a r r y @ c o r e w e b p r o g r a i r a n i n g . c o m " >


* L a r r y Brown</A>
* @version 2.0
V

public class Ship {


// Переменные экземпляра

private double x=0.0, y=0.0, speed=1.0, direction=0.0;


private String name;

// Конструкторы

/*^ Явное указание параметров при создании объекта Ship. */


220 Глава 7. Объектно-ориентированное программирование.

public Ship(double х, double у, double speed,


double direction, String name) {
setX(x);
setY(y);
setSpeed(speed);
setDirection(direction);
setName(name);
}
/** Создание объекта Ship с использованием значений
* по умолчанию(х=0, у=0, speed=1.0, direction=0.0).

public Ship(String name) {


setName(name);
}
/** Перемещение модели судна на один шаг. При перемещении
* используются текущие скорость и направление.

public void move() {


movelnternal(1);
}
/'^'^ Перемещение на N шагов. */

public void move(int steps) {


movelnternal(steps);

private void movelnternal(int steps) {


double angle = degreesToRadians(direction);
X = X + (double)steps * speed ^ Math.cos(angle);
у = у + (double)steps * speed ^ Math.sin(angle);
}
private double degreesToRadians(double degrees) {
return(degrees * Math.PI / 180.0);
}
/** Вывод данных о размещении в стандартный выходной поток. */
public void printLocation() {
System.out.println(getName() + " is at (" + getX() +
"," + getYO + " ) . " ) ;
}
/** Определение текущей позиции X. */
public double getX() {
return(x);
}

/•k-k Установка текущей позиции X. */

public void setX(double x) {


7.6. Подготовка классов к повторному использованию 221

this.x = х;
}
/** Определение текущей позиции Y. */

public double getY() {


return(у);

/•• Установка текущей позиции У. */

public void setY(double у) {


this.у = у;
}
/** Определение текущей скорости. */

public double getSpeedO {


return(speed);
}
/•• Установка текущей скорости. '^/

public void setSpeed(double speed) {


this.speed = speed;
}
/** Получение текущего направления (О=восток, 90=север,
* 180=запад, 270=юг). В данном случае вместо мореходной
* системы (0=север, 90=запад и т.д.) используются углы,
* принятые в математике.
V
public double getDirection() {
return(direction);

j-k-k Установка текущего направления (О=восток, 90=север,


* 180=запад, 270=юг).

public void setDirection(double direction) {


this.direction = direction;
}
/** Получение имени. Имя не может быть модифицировано
* пользователем.

public String getName() {


return(name);
}
private void setName(String name) {
this.name = name;
222 Глава?. Объектно-ориентированное программирование...

It'Hn^ffiPi'illfffflllHWIWIl
File Edk View Fjvofites Tools Help

[JiSIiJ Deprecated Help


eqCVClASS NCCrCtASS
SUMUA4V; 1ЧНИ irgtD lC<;tUS^a ШрЧ^Г'С OITAlL: riCiD ICOMSTB ЫеТЧОС'

Class Ship

I
+—Ship

public class $1ф


extends Object

Ship exunpk to demonstnte OOP in Java.

Itpj &yow}t

Constructor Sununaiy
iSli4fi<<iovaji* X, doubi* y , doubit s p « t d , doublt dirtction,
S'ibSLljUiil пале)
Build a sbp with specified perametere.
:SMp<S':vin:t n*ike)
\ Build a ship with default values (хЧЗ, y-0, sp«ed-l .0, di«ction-0.0).

Method Smmnary
d
; ^ My Compu»«
с
Рис. 7.1. Программа javadoc генерирует гипертек­
стовые документы по исходному коду Java

Листинг7.6. ShipTest.Java

p u b l i c c l a s s ShipTest {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ^ args) {
Ship s i = new S h i p ( " S h i p l " ) ;
Ship s2 = new S h i p ( 0 . 0 , 0 . 0 , г.Or 135.0, "Ship2");
s1.move();
s2.move(3);
si.printLocation();
s2.printLocation();
}
}

Компиляция и запуск:
javac ShipTest.Java
Java ShipTest
Первая из приведенных выше двух команд вызывает программу j a v a c для компи­
ляции файла S h i p . Java. Java-компилятор автоматически проверяет дату создания
7.7. Javadoc 223

каждого из файлов . c l a s s , которые используются компилируемым классом, сравни­


вает ее с датой создания исходного файла (если он дост)'пен) и при необходимости
выполняет повторную компиляцию файла. Таким образом, при работе с Java нет не­
обходимости использовать make-файлы; компилятор непосредственно п р о в е р я е т со­
ответствие файлов классов исходным файлам. Если вы хотите, чтобы файл . c l a s s
остался неизменным, несмотря на то, что файл . J a v a был модифицирован, перемес­
тите исходный файл в отдельный каталог.

На заметку
При работе с Java нет необходимости в применении таке-файлов.
Компилятор javac автоматически проверяет соответствие дат соз­
дания всех непосредственно связанных классов.

Результаты выполнения:
Shipl is at (1,0).
Ship2 i s a t ( - 4 . 2 4 2 6 4 , 4 . 2 4 2 6 4 ) .

7.7.Javadoc
п р о г р а м м а j a v a d o c распространяется в составе JDK, a также вместе с продуктами
независимых производителей. Эта утилита использует для генерации документации
комментарии, помещенные между последовательностями символов " / * * " и " * / " .
В состав документов могут включаться гипертекстовые ссылки на другие системные
классы и классы, определенные пользователем. О б ы ч н о комментарии j a v a d o c по­
мещаются перед определением класса, а также перед методами и переменными, в
объявлении которых не используется ключевое слово p r i v a t e . Первое предложение
в описании метода или переменной используется для ф о р м и р о в а н и я индекса вверху
страницы, а полное описание помещается в нижней части документа. Для построения
документации надо вызвать программу j a v a d o c и предать ей одно или несколько
имен файлов либо пакетов. Например:
javadoc Foo.java
javadoc Foo.java Bar.Java
javadoc graphics.newWidgets
javadoc graphics.newWidgets math.geometry
П р и вызове j a v a d o c можно также задавать различные опции. Допустимые опции
будут рассмотрены ниже в этом разделе, здесь же мы приведем лишь следующие про­
стые примеры:
javadoc -author -version SomeClass.Java
javadoc -noindex -notree somePackage
Рассмотрению пакетов посвящен раздел 7.10.
По умолчанию программа j a v a d o c использует для генерации HTML-данных стан­
дартный доклет-шаблоп. Доклет распознает в составе комментариев не только текст,
но и HTML-разметку. Поэтому вы можете использовать выражения типа <А
HREF=" . . . "> для ф о р м и р о в а н и я ссылок на Web-страницу организации и на другие
ресурсы, элементы IMG для включения копий экрана демонстрирующих программу в
224 Глава 7. Объектно-ориентированное программирование...

работе, и даже элементы APPLET, иллюстрирующие особо важные особенности ваше­


го класса. В состав javadoc-комментариев не стоит включать заголовки, поскольку
они могут нарушить общепринятую структуру документов, сгенерированных
j avadoc. Лишние пробелы, не включенные в состав элемента PRE, игнорируются.
Если вы хотите, чтобы документы генерировались в другом формате, вам надо на­
писать собственный доклет. Док)'ментацию по j a v a d o c и доклетам можно найти по
следующим адресам:

Исходная страница Javadoc 1.3


http://Java.sun.com/j2se/l.3/docs/tooldocs/javadoc/

Javadoc Tool Reference Page


http://Java.sun.com/j2se/l.3/docs/tooldocs/solaris/javadoc.html
http://Java.sun.com/j2se/l.3/docs/tooldocs/win32/javadoc.html

Общие сведения о доклетах и документация по API


http://Java.sun.com/j2se/l.3/docs/tooldocs/javadoc/overview.html
http://Java.sun.com/j2se/l.3/docs/tooldocs/javadoc/doclet/

Дескрипторы javadoc
в дополнение к элементам разметки HTML в j a v a d o c определены специальные
дескрипторы, которые включаются в состав комментариев для достижения требуе­
мых результатов. Так, например, дескриптор @param используется для описания па­
раметров, передаваемых методу, @return — для описания возвращаемых значений, а
@see — для определения ссылок на классы и методы, имеющие отношение к рассмат­
риваемом)^ элементу программы. Ниже приведен фрагмент комментариев, в котором
используются указанные дескрипторы.
/** Преобразование градусов в радианы.
* @param degrees Значение угла в градусах.
* @return Значение угла в радианах.
* @see t r a d i a n s T o D e g r e e s .

p u b l i c double degreesToRadians(double degrees) {


r e t u r n ( d e g r e e s * Math.PI / 1 8 0 . 0 ) ;
}

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


считается текст, помещенный между текущим и следующим дескрипторами либо ме­
жду дескриптором и окончанием комментариев.
Ниже описаны дескрипторы, которые могут быть помещены в состав коммента­
риев. Если не оговорено особо, считается, что дескриптор поддерживается JDK 1.1 и
более новыми версиями пакета.
7.7. Javadoc 225

©author
Д е с к р и п т о р @ a u t h o r задает сведения об авторе. Ч т о б ы данный дескриптор учи­
тывался при создании документа, необходимо задать команду " j a v a d o c - a u t h o r
. . .". В составе дескриптора @ a u t h o r допустимы HTML-элементы разметки, на­
пример:
/** Описание класса SomeClass

* @author <А HREF="mailto:ellison@microsoft.com">


* Larry Ellison</A>
V
©deprecated
Д е с к р и п т о р @ d e p r e c a t e d обозначает классы, поля и методы, которые не реко­
мендованы для дальнейшего использования, но продолжают поддерживаться для
обеспечения совместимости с предыдущими версиями. Не исключено, что в по­
следующих реализациях эти классы, поля и методы будут удалены.

{@docRoot}[Java 1.3]
Д а н н ы й дескриптор представляет относительный путь от сгенерированного доку­
мента до корневого док^'мента в иерархии пакетов. Этот дескриптор удобно при­
менять тогда, когда создается документация для большого пакета со многими под­
каталогами; с его помощью п р и н я т о ссылаться на страницу с лицензионными дан­
ными, расположенную в корневом каталоге. Например, если в документе
c o r e w e b p r o g r a m m i n g / S h i p . J a v a требуется ссылка на лицензионную информа­
цию, то выражение
<а href="{@docRoot}/license.html">
будет и н т е р п р е т и р о в а н о как
<а href="../license.html">
П р и этом предполагается, что файл licence.html находится в каталоге
corewebprograiraning.

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

{@link}Uaval.2]
Данный дескриптор аналогичен дескриптору 0 s e e , за исключением того, что вме­
сто раздела "See Also" (см. также) в документе генерируется гипертекстовая ссыл­
ка. Д а н н ы й дескриптор применяется в описаниях для создания ссылок на другие
методы. Н а п р и м е р , в описании S h i p . J a v a может содержаться приведенный ни­
же фрагмент.
То change the location of a ship use
{(Slink #move(int) move}.
226 Глава 7. Объектно-ориентированное программирование...

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


То change the location of a ship use
<a href="Ship.html#move(int)">move</a>.

@param
Дескриптор @param описывает параметры, передаваемые методу (или конструк­
тору). За дескриптором должно следовать имя параметра, за которым после про­
бела располагается описание этого параметра.

©return
Данный дескриптор описывает значение, возвращаемое методом.

@see
Дескриптор @see создает ссылки на описания других методов или классов. Перед
именем метода задаются имя класса и символ "#". Если метод принадлежит теку­
щему классу, имя класса не указывается. Например:

* @see #getWidget
* @see Widget
* @see Frob#setWidget
V
public void setWidget(Widget w) { ... }

@serial[Java 1.2]
@serialData[Java 1.2]
@serialField[Java 1.2]
Эти три дескриптора предназначены для того, чтобы описывать классы, допускаю­
щие вывод в поток ( S e r i a l i z a b l e ) . Первый дескриптор описывает поля, которые
подлежат записи в поток. Второй дескриптор описывает все поля ObjectStream
F i e l d , а третий задает последовательность и типы данных E x t e r n a l i z a b l e . Под­
робную информацию об этих дескрипторах вы найдете в документе h t t p : / / J a v a .
sun.com/j 2 s e / l . 3 / d o c s / g u i d e / s e r i a l i z a t i o n / s p e c / s e r i a l - a r c h . d o c 6 . h t m l .
©since
Данный дескриптор создает раздел "Since", используемый для документирования
добавляемых классов, полей или методов. Его удобно применять при описании
модифицируемых классов и пакетов, так как он позволят отличить вновь создан­
ные элементы от элементов, существующих давно.

©throwsljava 1.2]
Дескриптор @throws описывает исключение, генерируемое классом или методом.
За дескриптором указываются имя класса исключения, пробел и описание исклю­
чения.
7.7. Javadoc 227

©version
Д е с к р и п т о р A v e r s i o n используется при документировании класса и задает номер
версии. Ч т о б ы программа j a v a d o c учитывала данный дескриптор, ее надо вы­
звать следующим образом: " j a v a d o c - v e r s i o n . . . ".

Параметры командной строки javadoc


Программа j a v a d o c позволяет задавать опции, которые управляют ее выполне­
нием. Н а п р и м е р , чтобы разрешить обработку дескрипторов @ a u t h o r и @ v e r s i o n , а
также подавить индексирование и генерацию иерархии классов, можно использовать
следующее выражение.
javadoc -author -version -noindex -notree Class.Java
В JDK l.S при использовании стандартного доклет-шаблона доступны более 40 оп­
ций j a v a d o c . Н и ж е перечислены лишь наиболее часто применяемые опции.

-author
Данная опция )тсазывает j a v a d o c на то, что в результирующий документ должна быть
включена информация об авторе. По умолчанию эта информация игнорируется.

-bottom[Java 1.2]
Опция b o t t o m задает текст, который помещается в нижней части каждого выход­
ного д о к ) ^ е н т а . Текст располагается ниже навигационной строки. Если в тексте
содержатся пробелы, его надо заключить в кавычки.

-classpath
-sourcepath
Д а н н ы е опции указывают j a v a d o c , где надо искать заданные файлы . J a v a .
Для разделения каталогов в Solaris используется двоеточие, а в Windows — точка с
запятой. Каталоги, перечисленные в переменной окружения CLASS PATH, участву­
ют в поиске автоматически.

-d
Д а н н ы й флаг задает целевой каталог для HTML-файлов. В качестве значения мо­
жет быть приведено абсолютное либо относительное имя каталога. Если данная
опция не указана, HTML-файлы помещаются в тот же каталог, в котором находят­
ся исходные файлы. Часто с помощью опции - d задают каталог, в котором уже на­
ходится подкаталог i m a g e s , содержащий файлы, используемые в описаниях.

-encoding
-docencoding
Данная опция позволяет указать кодировку символов для исходного файла
( - e n c o d i n g ) либо для документа ( - d o c e n c o d i n g ) .
228 Глава 7. Объектно-ориентированное программирование...

-link [Java 1.2]


-linkoffline [Java 1.2]
Опция l i n k указывает j a v a d o c , где следует искать информацию для преобразо­
вания ссылок на другие пакеты. Например, при преобразовании ссылок на доку­
менты, содержащиеся на узле j a v a . s u n . c o m , используется следующее значение
опции l i n k :
-link http://Java.sun.com/j2se/l.3/docs/api
Реально программа j a v a d o c использует для преобразования ссылок на внешние
пакеты файл p a c k a g e - l i s t , расположенный по указанному выше URL. При от­
сутствии доступа к Internet для указания локальной копии файла p a c k a g e - l i s t
используется опция l i n k o f f l i n e . Если вы установили локальную копию докумен­
тации JDK 1.3, список Java-пакетов находится в каталоге / r o o t / j d k l . 3 / d o c s /
a p i . Чтобы преобразовать внешние ссылки на платформе Windows, надо исполь­
зовать следующее выражение:
-linkoffline http://Java.sun.com/j2se/l.3/docs/api
c:\jdkl.3\docs\api

-J
Инструмент j a v a d o c сам no себе является Java-программой. Опция - J позволяет
передавать параметры непосредственно в среду выполнения Java. Например, если
набор документов очень велик, вы можете задать большой (24 Мбайт) исходный
размер посредством следующего вырг1жения:
javadoc -J-ms24m . . .

-nodeprecated
-nodeprecatedlist [Java 1.2]
Первая из указанных опций сообщает javadoc о том, что записи о компонентах, не
рекомендованных для применения, должны быть исключены из документов. Вторая
опция запрещает создание документа d e p r e c a t e d - l i s t . h t m l и исключает ссылку
на компоненты, не рекомендованные для применения, из навигационной строки.
Информация о подобных компонентах генерируется и включается в документы.

-noindex
Данная опция указывает j a v a d o c на то, что документ AllNames . html не должен
создаваться. Индексирование удобно при описании пакетов. При создании доку­
ментации на отдельные классы его часто запрещают.

-nonavbar [Java 1.2]


Опция nonavbar запрещает генерацию навигационной строки в верхней и ниж­
ней части документа.

-notree
Данная опция указывает j a v a d o c на то, что иерархия классов t r e e . h t m l не
должна генерироваться.
7.7. Javadoc 229

-public
-protected
-package
-private
Данные опции позволяют указать, какие классы, поля и методы должны быть до­
кументированы. В приведенном выше перечне они расположены так, что каждая
последующая опция приводит к возрастанию объема данных в документе по срав­
нению с предыдущей. По умолчанию предполагается опция p r o t e c t e d , которая
задает документирование всех компонентов, объявленных как p u b l i c и p r o ­
t e c t e d . Такие описания чаще всего требуются пользователям, однако разработ­
чикам, которым приходится модифицировать содержимое классов, могут понадо­
биться данные о компонентах, объявленных как p r i v a t e .

-splitindex [Java 1.2]


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

-verbose
Данная опция указывает j a v a d o c на то, что при работе программы должна выво­
диться информация о времени, затраченном на разбор каждого файла.

-version
Опция v e r s i o n включает в документ номер версии, заданный с помощью деск­
риптора @ v e r s ion. По умолчанию номер версии не включается.

-windowtitle [Java 1.2]


Данная опция задает генерацию в составе HTML-документа заголовка, который
отображается в строке заголовка броузера. Текст, указанный в кавычках после
имени опции, помещается между дескрипторами < t i t l e > и < / t i t l e > созданного
HTML-документа. В заголовке недопустимы элементы разметки.
Приведенный ниже вызов утилиты j a v a d o c используется с JDK L3, установлен­
ным в системе Windows 98, и генерирует HTML-документ, показанный на рис. 7.L
>javadoc - a u t h o r - v e r s i o n -noindex - n o t r e e
-linkoffline
http://Java.sun.com/j2se/l.3/docs/api
c : \ j d k l . 3 \ d o c s \ a p i Ship.Java
Если число опциР! велико, общее количество символов может превысить максималь­
ную длину командной строки в конкретной системе. В таких случаях надо создать make-
файл для javadoc. Примеры make-файлов для Windows приведены в документе, распо­
ложенном по адресу h t t p : / / j a v a . s u n . c o m / p r o d u c t s / j d k / j a v a d o c / m a k e f i l e s .
Подобные файлы для других платформ можно найти в документе Javadoc Tool Reference
Page, расположенном по адресу h t t p : / / j a v a . s u n . e o m / j 2 s e / l . 3 / d o c s t o o l d o c s /
javadoc/index.html.
230 Глава 7. Объектно-ориентированное программирование.

7.8. Наследование
Наследование состоит в том, что новый класс создается на базе имеющегося клас­
са; при этом код существующего класса остается неизменным. Для обозначения на­
следования классов используется ключевое слово e x t e n d s . Исходный класс принято
называть родительским классом, или суперюшссом (в терминологии C++ он называется
базовым классом). Н о в ы й класс, созданный на основе существующего, называют до­
черним классом, или подклассом (в C++ такой класс называют порожденным). Конст­
руктор дочернего класса может непосредственно вызвать конструктор родительского
класса; для этого надо поместить в первой строке нового конструктора выражение
s u p e r . Ниже приведен код простого примера.
// ParentClass.Java: используется в роли суперкласса.
// Содержит поля а и Ь, а также методы foo и bar.
/ / В данном классе определены два конструктора.

public class ParentClass {


public int a;
private int b;

public ParentClass() { ... }


public ParentClass(double z) { ... }

public String foo(int x. String s) { ... }

private void bar() { ... }


}

// ChildClass.Java: подкласс класса ParentClass.


// Наследует поля a и b, a также методы foo и bar.
// Кроме того, в классе объявлены поле с и метод
// baz. Один из конструкторов ChildClass вызывает
// конструктор ParentClass.

public class ChildClass extends ParentClass {


public int c;

public ChildClass(double z) {
super(z); // Вызов конструктора ParentClass

p u b l i c void b a z ( b o o l e a n isReady) {
}

Помните, если конструктор подкласса непосредственно не вызывает конструктор


суперкласса посредством ключевого слова s u p e r , то перед выполнением первой ко­
манды конструктора подкласса автоматически вызывается конструктор суперкласса
без параметров. В этом случае, если в суперклассе не определен конструктор с нуле­
вым числом параметров, при компиляции отображается сообщение об ошибке. Как
вы уже знаете когда конструктор класса не определен, конструктор с нулевым числом
параметров создается автоматически. Если же разработчик определил хотя бы один
конструктор, другой конструктор автоматически не создается.
7 . 8 . Наследование 231

На з а м е т к у

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


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

Одним из преимуществ наследования является возможность переопределять в до­


черних классах методы родительских icTiaccoB. Метод, переопределяемый в дочернем
классе, должен иметь то же имя, то же число параметров и тот же тип возвращаемого
значения. П р и необходимости из метода дочернего класса может быть вызван метод
родительского класса. Для этого используется следующее выражение:
super. имя_переолределенного__метода (...)
Выражение s u p e r , s u p e r недопустимо, поэтому вызвать метод класса, который
выст)щает как родительский по о т н о ш е н и ю к суперклассу, невозможно. П р и необхо­
димости вы можете расширить область видимости переопределенного метода. На­
пример, если в родительском классе метод был определен как p r o t e c t e d , то в дочер­
нем классе его можно объявить как p u b l i c .
С наследованием связано одно распространенное заблуждение: многие считают,
что при обращении к любому методу родительского класса необходимо указывать
ключевое слово s u p e r . На самом деле оно необходимо только в том случае, когда ме­
тод с одним и тем же именем определен как в родительском, так и в дочернем классе,
т.е. когда метод переопределен в дочернем классе. Обращаться из дочернего класса к
методам, которые не были переопределены, можно с указанием только имени; клю­
чевое слово s u p e r не обязательно.

На з а м е т к у

Методы родительского класса доступны из дочернего класса по


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

П р и необходимости разработчик может также переопределять п е р е м е н н ы е роди­


тельского класса, однако на практике эта возможность никогда не используется. По­
добное переопределение приведет к тому, что в экземпляре дочернего класса появят­
ся две переменные с одинаковыми именами: одна будет использоваться при работе
методов дочернего класса, а другая — при выполнении методов родительского класса.
Такую программу очень трудно отлаживать.
Рассмотрим пример наследования и переопределения методов. В листинге 7.7 по­
казан класс S p e e d b o a t , в котором добавлены переменные и методы для работы с
цветом и переопределен метод p r i n t L o c a t i o n . Код, предназначенный для проверки
функционирования класса, приведен в листинге 7.8.
232 Глава 7. Объектно-ориентированное программирование.

Листинг 7.7. Speedboat.java

/** Специальный тип судна. Отображается красным цветом


* и по умолчанию развивает скорость 20 узлов. */

public class Speedboat extends Ship {


private String color = "red";

/** Конструктор класса Speedboat, для которого скорость


* и цвет принимаются по умолчанию. */

public Speedboat(String name) {


super(name);
setSpeed(20) ;
}
/** Конструктор класса Speedboat с параметрами. */

public Speedboat(double x, double y, double speed,


double direction. String name.
String color) {
super(x, y, speed, direction, name);
setColor(color);
}

/** Вывод сообщения о расположении. Метод переопределен. */

public void printLocation() {


System.out.print(getColor().toUpperCase() + " " ) ;
super.printLocation0;

/** Получение информации о цвете объекта Speedboat. */

public String getColor() {


return(color) ;

j-k-k Установка цвета объекта Speedboat. */

public void setColor(String colorName) {


color = colorName;
7.9. Интерфейсы и абстрактные классы 233

Листинг 7.8.SpeedboatTest.Java

/** Программа, использующая объекты Speedboat и Ship. */


public class SpeedboatTest {
public static void main(String[] args) {
Speedboat si = new Speedboat{"Speedboatl");
Speedboat s2 = new Speedboat(0.0, 0.0, 2.0, 135.0,
"Speedboat2", "blue");
Ship s3 = new Ship(0.0, 0.0, 2.0, 135.0, "Shipl");
s1.move();
s 2.move() ;
s 3.move();
si.printLocation()
s2.printLocation()
s3.printLocation()

Компиляция и запуск:
javac -depend SpeedboatTest.Java
Java SpeedboatTest
Первгш из приведенных выше команд автоматически обрабатывает файлы
S p e e d b o a t . j ava и S h i p . j ava.
Результаты выполнения:
RED Speedboatl is at (20,0).
BLUE Speedboat2 is at (-1.41421,1.41421).
Shipl is at (-1.41421,1.41421).
В отличие от C++ и L i s p / C L O S , в Java запрещено множественное наследование.
lOiacc может иметь сколь угодно подклассов, но только один суперкласс. В этом Java
похож на Smaltalk. Несмотря на то что работать с одним суперклассом очень удобно,
множественное наследование также имеет свои преимущества. В Java аналогичных
результатов можно добиться, применяя и н т е р ф е й с ы , которые будут рассмотрены в
следующем разделе.

7.9. Интерфейсы и абстрактные классы


Предположим, что вы собираетесь определить класс, который выступал бы в каче­
стве родительского для других классов, но хотите, чтобы пользователь не мог созда­
вать экземпляр этого класса. Разработчики поступают так в тех случаях, когда необхо­
димо задать в общих чертах поведение класса, но нет достаточной и н ф о р м а ц и и для
того, чтобы детально определить все особенности его работы. Подобный класс объ­
является с помощью модификатора a b s t r a c t как абстрактный, в результате при по­
пытке создать экземпляр этого класса компилятор отображает сообщение об ошибке.
В листинге 7.9 показан код абстрактного класса S h a p e . Подклассы этого класса будут
содержать методы, предназначенные для чтения и установки значений полей класса
(getX, g e t Y и т.д.), однако экземпляр класса S h a p e создать невозможно.
234 Глава 7. Объектно-ориентированное программирование...

Листинг 7.9. Shape.java

/•• Родительский класс для разнообразных форм. */

public abstract class Shape {


protected int x, y;

public int getX () {


return(x);
}
public void setX(int x) {
this.x = x;

public int getYO {


return(y);
}
public void setY(int y) {
this.у = у;
}
}

Java также позволяет создавать абстрактные методы — методы, в которых объяв­


ляются типы параметров и тип возвращаемого значения, а тело которых отсутствует.
public ReturnType methodName(Typel argl, Type2 arg2);
Классы, содержащие абстрактные методы, должны быть объявлены как абстракт­
ные. Если в подклассах этих классов не переопределяются все абстрактные методы,
подклассы также должны быть объявлены как абстрактные. Абстрактные методы по­
лезны в тех случаях, когда необходимо определить набор методов, задающих поведе­
ние всех подклассов данного класса, причем в разных подклассах эти методы должны
реализовываться по-разному.
Поскольк)^ в классе S h a p e абстрактные методы отсутствуют, его подклассы могут
быть как абстрактными, так и обычными классами. В листингах 7.10 и 7.11 показаны
коды двух абстрактных подклассов класса S h a p e . Один из них предназначен для
представления кривых, другой — для представления фигур, составленных из прямых
и отрезков.

Листинг 7 . 1 0 - C u r v e . j a v a

/ • * Форма, с о с т а в л е н н а я и з кривых (открытая или з а к р ы т а я ) .


* В п о д к л а с с ы будут включены с р е д с т в а для п о с т р о е н и я дуг
* и окружностей.
Ч

public abstract class Curve extends Shape {}


7.9. Интерфейсы и абстрактные классы 235

Листинг 7.11.StraightEdgedShape.Java

/*• Форма с углами (открытая или закрытая). Подклассами


* данного класса являются Line, LineSegment,
* LinkedLineSegments и Polygon.

public abstract class StraightEdgedShape extends Shape {}

Предположим теперь, что вам надо создать подкласс C i r c l e класса C u r v e , в ко­


тором будет присутствовать метод, предназначенный для вычисления площади фигу­
ры. Вы можете создать такой подкласс (не объявляя его как a b s t r a c t ) и реализовать
в нем метод g e t A r e а. Д а н н ы й подход вполне устроит вас до тех пор, пока круг оста­
ется единственной геометрической фигурой, площадь которого подлежит вычисле­
нию. Однако вы планируете также создать подкласс R e c t a n g l e класса
S t r a i g h t E d g e d S h a p e , площадь которого также можно вычислить. Т е п е р ь создание
универсальной программы, обрабатывающей площади фигур, вызывает затруднения,
поскольку у классов R e c t a n g l e и C i r c l e нет общего предка, содержащего метод
g e t A r e а. Предположим, например, что вам надо создать массив ф и г ) р , площади ко­
торых необходимо сложить. Массив C i r c l e [] не содержит прямоугольники, а мас­
сив R e c t a n g l e [ ] не может содержать круги. Массив S h a p e [ ] также не подходит для
указанной цели, поскольку^ в нем нет метода g e t A r e а. Если вам надо будет определить
фиг)ру с максимальной площадью, какой параметр вы передадите методу maxArea?
Это не может быть класс S h a p e , поскольку некоторые фигуры не имеют площади
(например, отрезки, д\ти и т.п.). Таким образом, работая с классами, не удается найти
простого решения данной проблемы.
Для решения подобных задач в Java предусмотрен механизм и н т е р ф е й с о в . Интер­
фейс представляет собой "полностью абстрактный" класс, в котором все методы яв­
ляются абстрактными. И н т е р ф е й с отличается от класса только тем, что класс может
реализовать несколько интерфейсов. Класс, реализующий и н т е р ф е й с , должен содер­
жать определения всех методов, объявленных в интерфейсе, либо быть определен
как абстрактный. Простой п р и м е р интерфейса приведен в листинге 7.12.

Листинг 7 . 1 2 . I n t e r f a c e l . j a v a

public interface Interfacel {


ReturnTypel methodl(ArgTypel arg)
ReturnType2 method2(ArgType2 a r g ) ;
}

В данном случае вместо c l a s s используется ключевое слово i n t e r f a c e , а тело


методов отсутствует; заголовок каждого метода заканчивается точкой с запятой (как в
случае абстрактных методов). Заметьте, что ключевое слово p u b l i c не указывается;
методы по умолчанию считаются общедоступными. Класс, реализующий этот интер­
фейс, показан в листинге 7.13.
236 Глава 7. Объе1стно-ориентированное программирование.

Листинг 7.13.Classl.Java

// Данный класс не является абстрактным, поэтому он должен


// содержать определения methodl и method2.

public class Classl extends SomeClass


implements Interfacel {
public ReturnTypel methodl(ArgTypel arg) {
someCodeHere();

public ReturnType2 rnethod2(ArgType2 arg) {


someCodeHere0;

Еще один интерфейс представлен в листинге 7.14.

Листинг 7.14. Interfасе2.java

public interface Interface2 {


ReturnType3 methods(АгдТуреЗ arg) ;

В листинге 7.15 приведен код класса, реализующего оба представленных выше ин­
терфейса.

Листинг 7.15,Class2,java

// Данный класс объявлен как abstract, поэтому в нем


// не обязательно реализовывать методы, объявленные в
// интерфейсах Interfacel и Interfасе2.

public abstract class Class2 extends SomeOtherClass


implements Interfacel,
Interface2 {

}
7.9. Интерфейсы и абстрактные классы 237

Конкретный подкласс класса C l a s s 2 показан в листинге 7.16.

Листинг 7 . 1 6 . C l a s s 3 . j a v a

/ / Данный класс не является абстрактным, поэтому он должен


/ / реализовывать методы methodl, methoci2 и methods.
p u b l i c c l a s s Class3 extends Class2 {
p u b l i c ReturnTypel methodl(ArgTypel arg) {
someCodeHere() ;

public ReturnType2 method2{ArgType2 arg) {


someCodeHere();

public ReturnTypeS methods(АгдТуреЗ arg)


someCodeHere() ;

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


не может быть создан. Однако интерфейс может расширять (но не реализовывать)
один или несколько других интерфейсов (если интерфейс расширяет несколько дру­
гих интерфейсов, они разделяются запятыми). Таким образом, подобно иерархии
классов, может быть создана иерархия интерфейсов. Интерфейс не может содержать
переменные экземпляра, но в нем могут быть определены константы. Ключевые сло­
ва p u b l i c , s t a t i c и f i n a l при определении констант предполагаются по умолча­
нию, поэтому они могут не указываться. Пример определения констант в составе ин­
терфейса приведен в листинге 7.17.

Листинг 7 . 1 7 . I n t e r f a c e s . J a v a

/ / Данный интерфейс наследует объявление трех методов.


/ / Кроме того, в нем определены две константы.
p u b l i c i n t e r f a c e I n t e r f a c e s extends Interfacel,
Interface2 {
i n t MIN_VALUE = 0;
i n t MAX VALUE = 1000;

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


Дело в том, что методы могупг ссылаться на интерфейсы так же, как и на реальные
классы. Вы можете создать интерфейс Measurable, показанный в листинге 7.18.
238 Глава 7. Объектно-ориентированное программирование.

Листинг 7.18.Measurable.j ava

/** Используется в классах, позволяющих вычислять


* площади фигур.

public interface Measurable {


double getAreaO;

Затем при определении класса Circle можно указать, что он реализует интер­
фейс Measurable (листинг 7.19).

Листинг 7.19.Circle.Java

/•• Круг (класс Circle). Поскольку площадь круга можно вычислить,


* класс Circle реализует интерфейс Measurable.

public class Circle extends Curve implements Measurable {


private double radius;

public Circle(int x, int y, double radius) {


setX(x) ;
setY(y);
setRadius(radius);
}
public double getRadius() {
return(radius);

public void setRadius(double radius)


this.radius = radius;
}
/** Обращения к интерфейсу Measurable. */

public double getAreaO {


returi> (Math. PI * radius * radius);

Наличие интерфейса Measurable позволяет ссылаться на классы, реализующие


этот интерфейс, не указывая конкретно, какие классы принадлежат к данной катего­
рии. Пример приведен в листинге 7.20.
7.9. Интерфейсы и абстрактные классы 239

Листинг 7.20.MeasureUtil.Java

/** Некоторые операции над классами, реализующим]^! интерфейс


'^ Measurable.
V
public class MeasureUtil {
public static double maxArea(MeasuraUDle ml,
Measurable m2) {
return(Math.max(ml.getArea(), m2.getArea()));
}
public static double totalArea(Measurable[] mArray) {
double total = 0;
for(int i=0; i<mArray.length; i++) {
total = total + mArray[i].getArea0;
}
return(total);
}
}

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


M e a s u r a b l e , автоматически становятся доступны методам класса M e a s u r e U t i l .
В качестве примера в листингах 7.21 и 7.22 показаны абстрактный класс P o l y g o n и
один из его конкретных подклассов. Все многоугольники имеют площадь, которая
может быть измерена, но для каждого типа многоугольника площадь вычисляется по
конкретному алгоритму.

Листинг7.21. Polygon.java

/ * * З а к р ы т а я форма с у г л а м и . */

p u b l i c a b s t r a c t c l a s s Polygon e x t e n d s StraightEdgedShape
implements Measurable {
p r i v a t e i n t numSides;

p u b l i c i n t getNumSides() {
return(numSides);

protected void setNumSides(int numSides) {


this.numSides = numSides;
}
}
240 Глава 7. Объектно-ориентированное программирование..

Листинг 7.22. Rectangle. java

/** В классе Rectangle определен метод getArea, объявленный


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

public class Rectangle extends Polygon {


private double width, height;

public Rectangle(int x, int y,


double width, double height) {
setNumSides (2);
setX(x);
setY(y);
setWidth(width);
setHeight(height);
}
public double getWidth() {
return(width);

public void setWidth(double width) {


this.width = width;
}
public double getHeightO {
return(height);

public void setHeight(double height) {


this.height = height;
}
/** Данный метод необходим, если класс реализует
* интерфейс Measurable.
Ч
public double getAreaO {
return(width * height);
}
}

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


не приводит к существенному увеличению объема кода в иерархии классов S h a p e .
Определение интерфейса M e a s u r a b l e занимает три строки, а для того, чтобы ука­
зать, что класс расширяет данный и н т е р ф е й с , в определении класса достаточно доба­
вить " i m p l e m e n t s M e a s u r a b l e " . Однако применение данного интерфейса экономит
много времени и усилий при создании класса M e a s u r e U t i l и делает его код нечувст­
вительным к изменениям, вносимым в классы, содержащие ^методы g e t A r e a . Простая
программа для проверки работы класса M e a s u r e U t i l приведена в листинге 7.2S.
7.9. Интерфейсы и абстрактные 1слассы 241

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


ют абсолютно все преимущества множественного наследования (способность класса
иметь более одного родительского класса), но гораздо проще в использовании. Даже
если учесть, что такие утверждения исходят от программистов, не имеющих доста­
точного опыта в применении множественного наследования, все равно следует при­
знать, что и н т е р ф е й с ы — удобный механизм, упрощающий р е ш е н и е многих задач.
И н т е р ф е й с ы буд\т часто встречаться далее в этой книге. Если же вы программирова­
ли прежде на языке, поддерживающем множественное наследование (например, C++,
Eiffel, L i s p / C L O S ) , вы, наверное, скажете, что Java не реализует все возможности, к
которым вы привыкли.

Листинг 7.23. MeasureTest.java

/** Проверка работы MeasureUtil. Заметьте, что в состав


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

public class MeasureTest {


public static void main(String[] args) {
Measurable[] measurables =
{ new Rectangle(0, 0, 5.0, 10.0),
new Rectangle(0, 0, 4.0, 9.0),
new Circle(0, 0, 4.0) ,
new Circle(0, 0, 5.0) };
System.out.print("Areas:");
for(int i=0; i<measurables.length; i++)
System.out.print(" " + measurables[i].getArea());
System.out.println();
System.out.println("Larger of 1st, 3rd: " +
MeasureUtil.maxArea(measurables[0] ,
measurables[2]) +
"\nTotal area: " +
MeasureUtil.totalArea(measurables));

С точки зрения пользователя класса (разработчика M e a s u r e U t i l ) интерфейсы


предпочтительнее; благодаря им класс может принадлежать различным иерархиям,
при этом не возникают проблемы, типичные для множественного наследования. Од­
нако с точки зрения разработчика класса и н т е р ф е й с ы мало чем могут помочь ему.
В частности, и н т е р ф е й с ы не позволяют наследовать реализации методов, что сокра­
тило бы время, требуемое для разработки. Предположим, наприхмер, что у вас есть
набор кнопок, окон и полей ввода текста, каждое из которых содержит поле с име­
нем, предназначенное для отладки, а также метод d e b u g , к о т о р ы й выводит данные о
расположении, родительском окне и имя компонента. Работая на Java, приходится
повторять этот код в каждом подклассе, в то время как множественное наследование
позволило бы сосредоточить всю необходимую логику в рамках родительского класса
(Debuggable).
242 Глава 7. Объектно-ориентированное программирование...

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


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

7.10. Пакеты, CLASSPATH и JAR-архивы


Java дает возможность объединять библиотеки классов в отдельные модули, или
пакеты. Это позволяет избежать конфликтов имен и упрощает процесс сопровожде­
ния. Например, если бы при работе над реальным проектом иерархии классов Ship и
Shape находились в различных пакетах, работать с ними было бы намного удобнее.
Для создания пакета с именем packagename надо создать каталог с таким именем и
поместить в него все исходные файлы. В начале каждого из файлов должно содер­
жаться следующее выражение:
package packagename;
Файлы, в которых декларация пакета отсутствует, автоматически помещаются в
неименованный пакет. Файлы, использующие пакет, должны содержать выражение
import packagename.ClassName;
или
import packagename.*;
Это выражение должно находиться перед определением класса, но после деклара­
ции пакета. Подобное выражение означает, что указанные классы (символ "*" поме­
чает все классы пакета) дост)'пны для использования. При отсутствии операции
import компилятор будет искать классы только в текущем каталоге и в каталогах, ука­
занных в переменной окружения CLASSPATH (переменная CLASSPATH будет рассмот­
рена ниже в этом разделе).
Имя пакета может содержать точку (". "). Компоненты имени, разделенные точка­
ми, соответствуют иерархии каталогов. Предположим, например, что ваше приложе­
ние разрабатывается в системе Windows 2000 или NT и находится в каталоге
С: \ J a v a \ c l a s s e s . Предположим также, что в процессе работы вы создали классы,
коды которых показаны в листингах 7.24-7.27, Эти классы размещены в следующих
пакетах.
• Пакетpackagel (С: \ J a v a \ c l a s s e s \ p a c k a g e l ) —содержит класс C l a s s l .
• Пакет package2 (С : \ J a v a \ c l a s s e s \ p a c k a g e 2 ) — содержит класс C l a s s 2 .
• Пакет р а с к а д е 2 . р а с к а д е З ( C : \ J a v a \ c l a s s e s \ p a c k a g e 2 \ p a c k a g e 3 ) — со­
держит класс C l a s s 3 .
• Пакетpackage4 (С: \ J a v a \ c l a s s e s \ p a c k a g e 4 ) — содержит класс C l a s s l .
Заметьте, что в пакетах p a c k a g e l и package4 находятся классы с одинаковыми
именами ( c l a s s l ) . Если оба этих класса должны применяться в одной программе,
следует отказаться от операции i m p o r t и использовать выражение типа имя_пакета.
C l a s s l . Такое имя называется полностью определенным именем класса. Так, например,
7.10. Пакеты, CLASSPATH и JAR-архивы 243

при получении доступа к классу A p p l e t можно каждый раз указывать полное имя
j a v a . a p p l e t . A p p l e t , a можно включить в файл выражение i m p o r t j a v a . a p p l e t .
A p p l e t (или i m p o r t J a v a , a p p l e t . *) и обращаться к классу по имени A p p l e t .
Обратите внимание, что при объявлении методов p r i n t l n f o указан модификатор
s t a t i c . Для вызова методов класса необязательно создавать экземпляр класса, доста­
точно указать имя класса (например, M a t h . c o s ) .

Листинг 7 . 2 4 . С : \ J a v a \ c l a s s e s \ p a c k a g e l \ C l a s s l . J a v a

package p a c k a g e l ;

public class Classl {


p u b l i c s t a t i c void p r i n t l n f o ( ) {
S y s t e m . o u t . p r i n t l n ( " T h i s i s C l a s s l in p a c k a g e l . " )
}
}

Листинг 7.25. С:\Java\classes\package2\Class2.java

package package2;

public class Class2 {


public static void printlnfo() {
System.out.println("This is Class2 in package2.");
}
}

Листинг 7.26. С:\Java\classes\package2\package3\Class3.java

package package2.раскадеЗ;

public class Class3 {


public static void printlnfo() {
System.out.println("This is Class3 in " +
"package2.раскадеЗ.");
}
}

Листинг 7.27.С:\Java\classes\package4\Classl.java

package package4;

public class Classl {


public static void printlnfo() {
System.out.println("This is Classl in package4."),
}
}
244 Глава 7. Объектно-ориентированное программирование.

Теперь напишем тестовую программу, использующую созданные классы (листинг


7.28). Эта программа размещается вне пакетов в каталоге С: \ J a v a X c l a s s e s .

Листинг 7.28.С:\Java\classes\PackageExample.Java

import packagel.*;
import package2.Class2;
import package2.packages.*;
p u b l i c c l a s s PackageExample {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
Classl.printlnfo() .
Class2.printlnfo() ,
C l a s s 3 . p r i n t l n f o (),
package4.Classl.printlnfo(
}

Компиляция и запуск:
j a v a c PackageExample.Java
Java PackageExample
Первая из приведенных выше команд автоматически компилирует все четыре
класса.

Результаты выполнения:
This is Classl in packagel.
This is Class2 in package2.
This is Class3 in package2.раскадеЗ.
This is Classl in package4.
В приведенном примере компиляция файла PackageExample. Java автоматиче­
ски влечет за собой компиляцию остальных исходных файлов, поскольку Р а с к а д е -
Example непосредственно обращается к соответствующим классам. Если вы хотите
скомпилировать исходные файлы по отдельности, вам надо указать в командной
строке также подкаталог.
javac packagel/Classl.Java
javac package2/Class2.Java
javac package2/package3/Class3.Java
javac package4/Classl.Java

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


матически, надо воспользоваться одним из двух следующих способов.
1. Запустить компиляцию из каталога, вышестоящего по отношению к каталогам
пакетов. Например, по команде j a v a c p a c k a g e 2 / C l a s s 2 . Java, вызванной из
каталога C : \ J a v a \ c l a s s e s \ , будет скомпилирован класс C : \ J a v a \ c l a s s e s \
p a c k a g e 2 \ C l a s s 2 . Java, находящийся в пакете package2.
7.10. Пакеты, CLASSPATH и JAR-архивы 245

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


в состав переменной окружения CLASSPATH и запустить процесс компиляции
из каталога, в котором находится исходный текст программы. Например, рабо­
тая с классами из приведенного выше примера, надо указать в составе
CLASSPATH каталог С: \ J a v a \ c l a s s e s .

Переменная окружения CLASSPATH


До сих пор мы считали, что все Java-файлы расположены в подкаталогах одного
каталога. Такой подход неприменим в том случае, когда в системе ведется работа над
различными приложениями. Java позволяет задать список каталогов для поиска клас­
сов. Этот механизм не заменяет пакеты, а дополняет их. В списке каталогов указыва­
ются только корневые каталоги иерархий пакетов; подкаталоги соответствуют от­
дельным пакетам. Таким образом, каталоги, указанные в списке задают, стартовые
точки для поиска классов, а выражение i m p o r t указывает, в каких подкаталогах нахо­
дятся эти классы. В большинстве систем Java допускается задавать в качестве старто­
вых точек для поиска классов JAR-архивы. П р и этом упрощается перемещение иерар­
хии классов из одного каталога в другой.
Путь для поиска классов задается двумя способами. Во-первых, вы можете указать
при вызове j a v a c или J a v a опцию - c l a s s p a t h . Во-вторых, набор каталогов можно
задать в качестве значения переменной окружения CLASSPATH. Этот способ исполь­
зуется разработчиками чаще всего. Если переменная CLASSPATH не установлена, сис­
тема Java ищет требуемые классы относительно текущего каталога. Очень часто про­
граммисты размещают классы в подкаталогах рабочего каталога; в этом случае в пе­
ременной окружения CLASSPATH, помимо прочего, надо указать текущий каталог (.).
Для разделения каталогов в списке используется (в зависимости от операционной
системы) двоеточие либо точка с запятой. Н а п р и м е р , в системе UNIX ( c s h , t c s h )
выражение, задающее значение CLASSPATH, имеет следующий вид:
setenv CLASSPATH .:-/classes:/home/mcnealy/classes
В Windows 9 8 / 2 0 0 0 / N T переменная окружения определяется следующим образом:
set CLASSPATH=.;С:\BillGates\classes;D:\Java\classes
В MacOS понятие переменных окружения отсутствует, но требуемый набор ката­
логов можно задать, изменяя ресурс 'STR МО). Многие системы Java, работающие в
среде Мае, обеспечивают более простые способы достижения требуемого эффекта.
В частности, в большинстве случаев нет необходимости явно указывать путь к стан­
дартным Java-классам; соответствующие значения задаются автоматически. Более то­
го, классы из пакета j a v a . l a n g . * импортируются по умолчанию.
Многие броузеры, в том числе Netscape и утилита a p p l e t v i e w e r , перед тем как
загрузить классы по сети, ищут их в каталогах, указанных в переменной окружения
CLASSPATH. Эти классы могут предоставлять аплету специальные привилегии. Если
злоумышленник каким-то образом ознакомится с классами на вашей машине, он мо­
жет написать аплет, которьп1, будучи загруженным по сети, выполнит от вашего име­
ни недопустимые действия. Поэтому перед запуском броузера убедитесь, что пере­
менная окружения CLASSPATH не установлена.
246 Глава 7. Объектно-ориентированное программирование...

Безопасность
Перед запуском броузера отключите установки CLASSPATH.

Использование JAR-файлов
Установка переменной окружения CLASSPATH— наиболее подходящий способ за­
дать список каталогов, в которых должен осуществляется поиск файлов классов. Од­
нако, если вы используете большое количество пакетов, размеры CLASS PATH могут
превысить максимальные размеры, допустимые для переменной окружения в опера­
ционной системе. Для того чтобы разрешить эту проблему, Java позволяет включать
классы в Java-архив (JAR). В состав JDK входит утилита j a r , которая помещает файлы
классов в сжатом виде в единый JAR-файл. П р и этом используется алгоритм, анало­
гичный сжатию в ZIP-формате.
Ниже перечислены опции, которые наиболее часто задаются п р и вызове j а г .
с
Данная опция задает создание нового JAR-файл а.
f
Опция f указывает файл для обработки или целевой JAR-файл.

т, М
Первая из приведенных опций (строчная ш) указывает на то, что для поддержки
расширенных возможностей (подписанные JAR-файлы, исполняемые JAR-файлы)
используется файл описания (manifest file). Если указаны и m и f, порядок исполь­
зования файла описания и JAR-файла совпадает с порядком следования опций.
Вторая опция (прописная М) указывает на то, что файл описаний не должен созда­
ваться. П о умолчанию описание включается в архив; ознакомиться с его содержи­
мым можно, разархивировав JAR-файл. Дополнительную и н ф о р м а ц и ю о файлах
описаний вы найдете по адресу h t t p : / / J a v a . s u n . c o m / d o c s / b o o k s / t u t o r i a l /
jar/basics/manifest.html.

t
Эта опция выводит содержимое JAR-файла.
u
Данная опция указывает на обновление JAR-файла, например добавление файлов
или изменение описания.
V
Опция V задает вывод подробного отчета о выполненных действиях.

хфайя
Данная опция распаковывает файлы, входящие в состав JAR-файла. Если файл не
указан, распаковываются все файлы в архиве.
7 . 1 1 . Модификаторы 247

О
Указанная опция (цифра "нуль") задает создание несжатого JAR-файла.
Н и ж е в качестве примера приведена команда, с помощью которой все ф а й л ы
C l a s s X . c l a s s , рассмотренные в рамках предыдущего примера, помещаются в один
JAR-файл.
С:\Java\classes>jar cfv e x a m p l e . j a r p a c k a g e l package2 package4

Результаты выполнения:
added manifest
adding: packagel/(in = 0) (out= 0)(stored 0%)
adding: packagel/Classl.class(in = 4 68)
(out= 326)(deflated 30%)
adding: package2/(in = 0) (out= 0)(stored 0%)
adding: package2/Class2.class(in = 4 68)
(out= 327)(deflated 30%)
adding: package2/package3/(in = 0) (out= 0)
(stored 0%)
adding: package2/package3/Class3.class(in = 486)
(out= 338)(deflated 30%)
adding: package4/(in = 0) (out= 0)(stored 0%)
adding: package4/Classl.class(in = 4 31)
(out= 298)(deflated 30%)
В данном примере мы предполагаем, что в каталогах, соответствующих пакетам,
содержатся только файлы классов. Если это не так, в архив e x a m p l e . j a r будут вклю­
чены также исходные тексты программ и все другие файлы. Для того чтобы в архив
помещались только файлы классов, это надо явно указать в командной строке. П р и
необходимости вы можете поместить список jar-команд в отдельный файл и затем за­
дать его в командной строке в виде (? имя_файла.
Создав jar-файл, можно указать его в качестве значения переменной CLASSPATH.
Если строка все же оказывается слишком длинной, файл следует поместить в каталог
/ r o o t / j d k l . 3 / j r e / l i b / e x t / . JAR-файлы, расположенные в этом каталоге, Java
проверяет автоматически, как и каталоги, явно указанные в списке поиска классов.
На з а м е т к у

Если длина строки, заданной в качестве значения переменной


CLASSPATH, слишком велика, вы можете разместить иЛЙ-файлы в ка­
талоге /root/jdkl.3/jre/lib/ext/. JAR-файлы, содержащиеся в
этом каталоге, проверяются автоматически.

7 . 1 1 . Модификаторы
в данной главе при объявлении классов, методов и переменных экземпляров ис­
пользовались различные модификаторы: p u b l i c , p r i v a t e , p r o t e c t e d , s t a t i c и др.
Несмотря на то что смысл этих модификаторов был ясен из контекста, желательно
рассмотреть их подробнее.
248 Глава 7. Объектно-ориентированное программирование.

Модификаторы области видимости


Эти модификаторы определяют, какие из классов могут иметь доступ к данным в
составе создаваемого вами объекта. Если вы еще не знакомы с объектно-
ориентированным программированием, вам на первое время достаточно будет разо­
браться с назначением модификаторов p u b l i c и p r i v a t e и не обращать внимания
на остальные. Если вы уже программировали на C++, можете сравнить классы, при­
надлежащие одному пакету, с дружественными классами C++.

Таблица 7.1. Модификаторы области видимости


Модификатор переменной или метода

Возмоэнтости доступа public, private protected Модификатор


к переменным и методам отсутствует
(по умолчанию)

Тот же класс Да Да Да Да

Классы в том же пакете Да Нет Да Да

Подклассы Да Нет Да Нет

Классы из других Да Нет Нет Нет


пакетов

public
Данный модификатор указывает на то, что переменная или метод доступны любо­
му, кто имеет доступ к экземпляру класса. Данным модификатором помечаются
средства, предоставляемые пользователям; они обычно документируются с помо­
щью j a v a d o c .
Многие разработчики считают, что переменные экземпляра никогда не должны
объявляться как общедоступные и что обращаться к ним следует только посредст­
вом методов. Аргументы в пользу такого мнения вы найдете в одном из предыду­
щих разделов. Подобный подход использовался при создании примера S h i p
(листинг 7.5); такое соглашение действует и для компонентов JavaBeans. Однако
некоторые программисты утверждают, что "никогда" — это слишком строгое тре­
бование и что общедоступные поля вполне допустимы для небольших классов, ис­
пользуемых как записи (подобно структурам в языке С). Для таких классов, счита­
ют они, методы доступа лишь устанавливают и читают значения переменных, не
выполняя никаких проверок, поэтому совершенно излишни. В качестве примера
подобного класса можно привести класс J a v a . a w t . P o i n t , который содержит пе­
ременные X и у, объявленные как p u b l i c , и который с самого начала задумывался
как объект, объединяющий две целочисленные переменные. В качестве компро­
миссного решения можно порекомендовать избегать объявления переменных как
p u b l i c в классах со сложной структурой.
7 . 1 1 . Модификаторы 249

М е т о д и к а профессионалов

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


ляров как public.

Модификатор p u b l i c в определении класса означает, что его могут загружать и


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

private
Этот модификатор означает, что переменная или метод доступны только для ме­
тодов того же класса. Модификатор p r i v a t e обычно используется для определе­
ния тех методов и переменных, к о т о р ы е необходимы для реализации логики клас­
са, но которые не должны быть доступны пользователям.

protected
Д а н н ы й модификатор указывает на то, что переменная или метод могут быть дос­
тупны методам того же класса и классов, принадлежащих тому же пакету. Пере­
менные и методы, объявленные как p r o t e c t e d , могут быть видимы из-за границ
пакета посредством механизма наследования. Этот модификатор обычно прим
няется при определении тех методов и данных, которые в обычных условиях рас­
сматриваются как p r i v a t e , но могут представлять интерес для программистов,
разрабатывающих дочерние классы.

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

Прочие модификаторы
static
Д а н н ы й модификатор означает, что переменная или метод совместно использу­
ются всеми экземплярами класса. П р и обращении к переменным и методам, объ­
явленным как s t a t i c , указывается либо экземпляр класса, либо имя класса. Так,
например, если в классе Foo объявлена переменная класса b a r и созданы два эк­
земпляра класса f o o l и f о о 2 , то для обращения к переменной b a r может исполь­
зоваться одно из следующих выражений: f o o l . b a r , f оо2 . b a r и Foo , b a r . Все три
выражения обеспечивают доступ к одной и той же разделяемой переменной. В ка­
честве примера метода класса (статического метода) можно привести M a t h , c o s ,
где Math — имя класса, а c o s — имя метода.
Статические методы могут обращаться только к переменным класса и статическим
методам другого класса. Чтобы иметь возможность работать с прочими компонен­
тами, они должны создавать экземпляры соответствующих классов. В листинге 7.29
250 Глава 7. Объектно-ориентированное программирование..

показан код метода класса main. Этот метод может непосредственно обращаться к
статическому методу s t a t i c M e t h o d , но для обращения к методу regularMethod он
должен создать экземпляр класса.

pmi
Листинг 7.29. Statics.java

public class Statics {


public static void main(String[] args) {
StaticMethod{);
Statics si = new Statics();
si.regularMethod{) ;

public static void staticMethod() {


System.out.println("This is a static method.");
}
public void regularMethod() {
System.out.println("This is a regular method.");
}
}

final
Приведенный в определении класса модификатор f i n a l означает, что класс не
может иметь подклассов. Такие сведения позволяют компилятору оптимизировать
вызовы методов и действия с переменными. Если модификатор f i n a l присутству­
ет в объявлении переменной, он указывает, что ее значение не может изменяться
в процессе выполнения и она не может быть переопределена в подклассах. Таким
образом, переменная, объявленная как f i n a l , —это константа.

abstract
Данный модификатор применим к классам и методам и означает, что экземпляр
класса не может быть создан. Примеры использования модификатора a b s t r a c t
приведены в разделе, посвященном интерфейсам и абстрактным классам.

synchronized
Модификатор s y n c h r o n i z e d применяется для блокировки методов при многопо­
токовом программировании. Метод, объявленный как s y n c h r o n i z e d , не может
одновременно использоваться двумя потоками. Дополнительную информацию о
работе с потоками вы найдете в главе 16.

volatile
Для эффективной работы многопотоковых программ Java позволяет методам соз­
давать локальные копии переменных экземпляров, согласовывая вносимые изме­
нения. Для некоторых типов данных (например, long) при выполнении многопо­
токовых программ возникает риск частичного обновления переменной одним по­
током перед обращения к ней другого потока. Модификатор v o l a t i l e предотв­
ращает возникновение подобной ситуации.
7 . 1 1 . Модификаторы 251

transient
П е р е м е н н ы е , определенные как t r a n s i e n , не подлежат сериализации при записи
объектов на диск или передаче их по сети.

native
Данный модификатор указывает на то, что метод реализован на языке С или C++.

7.12. Резюме
Java— это объектно-ориентированный язык программирования общего назначе­
ния. Не понимая основных принципов использования объектов, невозможно создать
программу на Java. В данной главе были описаны создание объектов, определение их
состояния (работа с переменными экземпляров) и поведения (создание методов). Пу­
тем наследования можно строить иерархии классов, не повторяя фрагменты кода,
совместно используемые подклассами. Несмотря на то что в Java запрещено множест­
венное наследование, и н т е р ф е й с ы предоставляют удобные средства для работы с
объектами, принадлежащим различным иерархиям классов. Классы, предназначен­
ные для повторного использования, документируются с помощью j a v a d o c и для
удобства работы объединяются в пакеты.
Для тех программистов, которые никогда не работали с объектами, применение
средств Java сопряжено с определенными трудностями. Чтобы чувствовать себя ком­
ф о р т н о при программировании объектов, надо освоить ряд новых понятий, т.е. сде­
лать своеобразный "концептуальный рывок". Для освоения материала, изложенного в
главе 8, такой "рывок" не нужен; эта глава представляет собой описание базовых кон­
струкций Java. Если вы ранее разрабатывали программы на C++, вы можете бегло
прочитать эт)^ главу, отмечая для себя отличия Java от известного вам языка.
СИНТАКСИС JAVA

В ЭТОЙ главе...

Простые типы.
Арифметические, логические операторы и операторы
сравнения.
Условные операторы и циклы.
Математические методы.
Ввод с клавиатуры и вывод на консольный терминал.
Выполнение программ, написанных на языках,
отличных от Java.
Ссылки.
Строки.
Массивы.
Векторы.
Пример построения бинарного дерева.
Исключения.
Sl/\ZJ^ZJ

еперь, когда вы имеете общее представление об объектно-ориентированном

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


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

8 . 1 . Синтаксические правила
Приступая к написанию реальных программ и намереваясь понять сообщения об
ошибках, выводимых компилятором, желательно знать "правила игры". В данном
разделе описаны некоторые лексические и грамматические правила Java.
Используя комментарии и выделяя фрагменты кода с помощью пробелов, симво­
лов табуляции и перевода строки, можно добиться того, чтобы программа стала более
удобочитаемой. В Java поддерживаются блоки комментариев (текст, расположенный
между парами символов " / * " и " * / " ) и строки комментариев (текст, находящийся ме­
жду парой символов " / / " и концом строки).
Пробелы, символы табуляции и перевода строки используются для отделения фраг­
ментов кода друг от друга. С их помощью можно выделить выражение в отдельную
строку, отделить имя функции от параметров или расположить математические опера­
торы в формате, удобном для чтения. Кроме того, с помощью пробелов и знаков табу­
ляции можно сформировать отст)'пы в операторах цикла и условных операторах. При­
мер Java-кода, использующего комментарии, пробелы и отстутгы, показан ниже.
254 Глава 8. Синтаксис Java

/* Блок комментариев. Текст может занимать несколько


строк. Вложение блоков не допускается. */

// Строка комментариев

while (int i=l; i<=5; i++) {


if (i==2)
System.out.println("Tea for two.");
else
System.out.println("Not two.");
}
Bee комментарии и большинство пробелов можно удалить. Следующий фрагмент
кода полностью идентичен предыдущему и будет преобразован компилятором Java в
тот же байтовый код:
while(int i=l;i<=5;i++){if(i==2)
System.out.println("Tea for two.");else
System.out.println("Not two.");}
Возможно, вы спросите: "Зачем нужен пробел в выражении ' i n t i'?" Дело в том,
что данный пробел разделяет две лексемы: ключевое слово i n t и имя переменной i.
Подобно тому, как предложения любого языка состоят из слов, Java-программы со­
стоят из лексем, таких как числа, имена переменных, знаки пунктуации и специаль­
ные ключевые слова. Как и при формировании предложений языка, для построения
однозначно интерпретируемых компьютерных программ также должны использо­
ваться грамматические правила. Разделение на лексемы управляется следующими
правилами.
1. В лексемах учитывается регистр символов. Например, лексема While или
WHILE не эквивалентна ключевому слову w h i l e .
2. Идентификаторы (имена переменных и методов) должны начинаться с буквы
либо с символа подчеркивания ("_") и содержать буквы, числа и знаки подчер­
кивания. Обычно в именах, в которых допустим знак подчеркивания, можно
использрвать и знак "$", однако этого лучше не делать, поскольку некоторые
компиляторы интерпретируют данный символ специальным образом.
Для разделения различных частей кода используются точки, запятые, круглые,
квадратные и фигурные скобки, а также другие знаки пунктуации. Эти символы назы­
вают разделителями. В частности, точка с запятой означает конец выражения, запя­
тые отделяют друг от друга параметры в вызове метода, скобки используются для
группировки компонентов арифметических выражений, фигурные скобки отмечают
начало и конец программного блока, квадратные скобки применяются для обозначе­
ния массивов.
Подробно синтаксис Java описан в документе The Java Language Specification,
электронная копия которого находится по адресу h t t p : / / j a v a . s u n . c o m / d o c s /
books/jls/.
8 . 2 . Простые типы 255

8.2. Простые типы


В Java определены две категории типов данных, простые т и п ы , или примитивы, и
ссылки. К простым типам относятся типы, не являющиеся объектами, — целые, сим­
волы, числа с плавающий т о ч к о й и др. В Java существует восемь простых типов:
b o o l e a n , c h a r , b y t e , s h o r t , i n t , l o n g , f l o a t и d o u b l e . Кроме того, специальный
тип v o i d указывает на то, что метод не возвращает значения.

boolean
Этот тип данных может принимать одно из двух значений: t r u e или f a l s e . В от­
личие от C/C++, в Java логический тип отличается от целого. Например:
boolean flagl = false;
boolean flag2 = (6 < 7 ) ; // true
boolean flag3 = !true; // false
boolean flag4 =0; // Сообщение об ошибке при компиляции!

char
Содержит 16-битовое целое число, обозначающее символ в формате Unicode.
Символьное значение может представлять собой символ в одинарных кавычках,
шестнадцатеричный код unicode-символа (\uXXXX, где X— шестнадцатеричная
цифра) либо специальный символ \ Ь (Baskspace), \ t (табуляция), \ п (перевод
строки), \ f (перевод ф о р м а т а ) , \ г (возврат каретки), \ » (двойная кавычка), \ '
(одинарная кавычка), \ \ ( о б р а т н а я косая). Например:
char сО= 3 ;
char с1 = ' Q ' ;
char с2 = ' \uOOOO'; / / Наименьшее з н а ч е н и е
char сЗ = ' \ u F F F F ' ; / / Наибольшее з н а ч е н и е
char с4 = ' \ Ь ' ; / / Backspace
Описание стандарта Unicode можно найти по адресу h t t p : //www. U n i c o d e . o r g / .

byte
Данный тип представляет 8-битовое целое число со знаком. Для определения от­
рицательных чисел используется дополнение до двух. Диапазон значений пере­
менной типа b y t e — от -128 до -«-127.

short
Тип s h o r t представляет 16-битовое целое число со знаком. Для определения от­
рицательных чисел используется дополнение до двух. Диапазон значений пере­
менной типа s h o r t — от -32768 до +32767.

int
Этот тип представляет 32-битовое целое число со знаком. Для определения отри­
цательных чисел используется дополнение до двух. Целые числа можно задавать в
десятичной системе счисления (числа 1, 10 и 100 представляются как 1, 10 и 100),
восьмеричной системе с префиксом О (числа 1, 10 и 100 представляются как 0 1 ,
256 Глава 8. Синтаксис Java

012 и 0144) либо в шестнадцатеричной системе с префиксом Ох (числа 1, 10 и 100


представляются как 0x1, ОхА и 0x64). Шестнадцатеричные числа, превышающие 9,
обозначаются прописными латинскими буквами А, В, С, D, Е и F. Допустимый диа­
пазон значений переменной типа i n t — от I n t e g e r .MIN_VALUE (-2 ) до
I n t e g e r . MAX_VALUE (2"' -1). Например:
int iO = 0;
int il = -12345;
int i2 = OxCafeBabe; // "Магическое" число файлов .class
int 13 = 0777; // Восьмеричное число,
// равное 511 в десятичной системе

long
Д а н н ы й тип представляет 64-битовое целое число со знаком. Для определения от­
рицательных чисел используется дополнение до двух. Длинные целые литералы
обозначаются буквой L, которая располагается после числового значения. Буква 1
нижнего регистра также поддерживается компилятором, но применять ее не ре­
комендуется, поскольку при выводе на печать строчная буква 1 похожа на цифру 1.
Значения можно задавать в десятичном, восьмеричном и шестнадцатеричном
форматах. Допустимый диапазон значений переменной типа l o n g — от
L o n g . MIN_VALUE (-2"") до Long . MAX_VALUE (2''-1). Например:
long аО = 0L;
long al = -123451;
long а2 = OxBabeL; // Это не слово, а число
long аЗ = -067671;
За исключением c h a r , все целочисленные т и п ы ( b y t e , s h o r t , i n t и l o n g ) яв­
ляются знаковыми. В отличие от С и C^-^-, в Java не поддерживаются беззнаковые
целые. Поэтому, используя целочисленные т и п ы (особенно b y t e и s h o r t ) , следу­
ет следить, чтобы результат вычислений не превысил максимально допустимое
значение для данного типа. Так, например, если вы хотите представить день в го­
ду, тип b y t e для этого не подходит, поскольку максимальное положительное чис­
ло, которое может содержаться в переменной типа b y t e , равно 127. Вместо этого
вам надо использовать s h o r t , i n t или l o n g . Как правило, тип i n t вполне подхо­
дит для большинства приложений.

float
Тип f l o a t представляет 32-битовое число с плавающей точкой в ф о р м а т е IEEE
754. Для обозначения литералов с плавающей точкой одинарной точности после
числового значения указывается буква f либо F. В приведенном ниже примере бу­
ква f необходима, поскольку в противном случае константа будет интерпретиро­
вана как число с плавающей точкой двойной точности, а при присвоении пере­
менной типа f l o a t значение будет усечено.
float fO = - 1 . 2 3 f ;
Для указания степени числа 10 используется буква е либо Е. Н а п р и м е р , в первом
из приведен1гых ниже примеров переменной f l присваивается число 6.02x10"^ , а
во втором примере переменной f 2 присваивается число 4.5x10 .
8.2. Простые типы 257

float fl = 6.02E23F;
float f2 = 4.5e-17f;
Диапазон значений чисел с плавающей точкой лежит в интервале от
F l o a t . MIN_VALUE (1.4x10"'") до F l o a t .MAX_VALUE (3.4х10''). П р и вычислениях
значений с плавающей точкой исключения генерируются только в случае деления
на нуль. В классе F l o a t определены константы специального назначения —
POSITIVE_INFINITY, NEGATIVE_INFINITy и NaN (not-a-number не является чис­
лом). Для сравнения со значением NaN следует использовать F l o a t . isNaN. Выра­
жение ( F l o a t . NaN == F l o a t . NaN) всегда возвращает значение f a l s e . В специ­
фикации IEEE определено, что данное значение должно возвращаться при любом
сравнении с NaN.

double
Данный тип представляет 64-битовое число с плавающей точкой в формате IEEE
754. Для обозначения литералов с плавающей точкой двойной точности можно
использовать d либо D, однако в большинстве случаев эта буква не указывается.
Н е к о т о р ы е п р и м е р ы приведены ниже.
double dO = 1.23;
double d2 = -4.56d;
double d3 = 6.02214E+23;
double d4 = le-99;
Диапазон значений чисел с плавающей точкой двойной точности лежит в интер­
вале от D o u b l e . MIN_VALUE (4.9х10'''') до D o u b l e .MAX_VALUE (1.7х10'"'). Как и в
случае с f l o a t , при вычислениях с двойной точностью не генерируются исключе­
ния. В классе D o u b l e определены константы специального назначения —
POSITIVE_INFINITY, NEGATIVE_INFINITY и NaN. Для сравнения со значением
D o u b l e . NaN следует использовать D o u b l e . isNaN.

Преобразование простых типов


При преобразовании простого типа с большим диапазоном значений в тип с мень­
шим диапазоном значений в языке Java необходимо явно указывать тип. Например:
Туре2 t y p e 2 V a r = (Туре2)typelVar;
B подобных случаях может происходить усечение с потерей старших разрядов,
однако, даже если это произойдет, исключение не возникнет. Например:
int 1 = 3 ;
byte b = (byte)i; // Приведение i к байтовому типу;
// значение переменной не изменяется
long X = 123456L;
short s = (short)х; // Приведение х к типу short с потерей
// старших разрядов
Заметьте, что в последнем примере значение s не равно значению х, поскольку
число 123456 невозможно представить с помощью 16-битового целого. Как и при
преобразовании к типу с меньшим диапазоном значений, преобразование числа с
плавающей точкой в целое число также требует явного указания типа.
258 Глава 8. Синтаксис Java

double d = 3.1416;
float f = (float)d; // Преобразование для хранения
// в 32-битовой переменной
short S = (short)f; // Преобразование float в short
int i = s; // Преобразование в тип с большим диапазоном
// значений; явного указания типа не требуется
Для того чтобы вместо усечения производилось округление, надо использовать
соответствующие методы класса Math, к о т о р ы е будут рассмотрены ниже в этой главе.
Заметьте, что числовые значения не могут быть преобразованы в логические, а логи­
ческие — в числовые. Ситуации, при которых требуется явное преобразование типов,
подробно описаны в д о к у м е н т е / a f а Language Specification, который находится по адре­
су h t t p : / / J a v a . s u n . c o m / d o c s / b o o k s / j I s / .

8.3. Операторы, условные операторы и циклы


В данном разделе описаны арифметические операторы (+, - , * и т.д.), условные
о п е р а т о р ы ( i f и s w i t c h ) и циклы ( w h i l e и f o r ) . В некоторых языках пользователь
может переопределять встроенные о п е р а т о р ы для обозначения действия с классами
(выполнять перегрузку операций). Так, например, следующий фрагмент кода C++ вы­
полняет сдвиг одной точки относительно другой:
Point pl(3,4)f р2(1,3); // Конструктор C++
р1 += р2; // Сдвиг р1 относительно р2
В Java перегрузка операций не поддерживается. Для выполнения действий над
классами, определенными пользователем, вызываются методы.
р1.translate(р2);

Арифметические операторы
Основные операторы для работы с числами приведены в табл. 8.1. Вы, вероятно, за­
метите, что в ней отсутствует оператор возведения в степень. Не стоит беспокоиться по
этому поводу: соответствующие операции реализованы посредством методов класса
Math. Заметьте также, что оператор + может использоваться для конкатенации строк.

Таблица 8 . 1 . Арифметические операторы

Оператор Описание Пример


+, - Сложение, вычитание X = у + 5;
* / % Умножение, деление, остаток от i n t X = 3, у = 2;
деления i n t Z = X / у;
/ / П р и д е л е н и и целых ч и ­
сел остаток т е р я е т с я
П р и б а в л е н и е / в ы ч и т а н и е единицы i n t i = 1, j = 1 ;
до/после выполнения операции ^^^ ^ ^ i++/ / / х = 1 , 1=2
i n t у = ++J; / / у=2, j=2
8.3. Операторы, условные операторы и циклы 259

Окончание табл. 8.1

« / »/ >» Сдвиги с учетом и без учета знака i n t х = 3;


int у = X « 2; / / 12
Побитовая инверсия i n t х = ~127; / / -128

Scf \f ^ Побитовые операции And, Or, Хог i n t х = 127 & 2; / / 2


i n t у = 127 I 2; / / 127
i n t z = 127 ^ 2; / / 125

Java, как и C++, позволяет записывать следующие выражения:


переменная операция = переменная;

что является сокращенно формой записи


переменная = переменная операция переменная;
Например, вместо
1=1+5;
X = X + 3;

можно записать
1 += 5;
X += 3 ;

Условные операторы
в языке Java определены условные операторы if, switch и "? :", которые приве­
дены в табл. 8.2 и детально описаны в этом разделе. Если вы знакомы с С или C++, мо­
жете не уделять этому материалу много внимания, поскольку данные операторы поч­
ти идентичны соответствующим операторам C/C++, за исключением того, что усло­
вия задаются как логические значения. Логические операторы, которые часто
используются в сочетании с условными операторами, приведены в табл. 8.3.

Таблица 8.2. Условные операторы

Оператор Стандартная форма


if if {логическое_выражение) {
выражение;
}
if (логическое_выражение) {
выражение!/
} else {
выражение2;
}
?: логическое_выражение? значение! : значение2;
260 Глава 8. Синтаксис Java

Окончание табл. 8.2


g^.^j^j^ s w i t c h (целое) {
c a s e з н а ч е н и е ! : выражение!;
breaks-
c a s e з н а ч е н и е 2 : выражение2а;
выражение2Ь;
breaks-
d e f a u l t : выражениеЫ;
}

Таблица 8.3. Логические операторы

Оператор Описание
==, != Равенство, неравенство. Помимо сравнение двух простых типов,
== позволяет выявить, указывают ли две ссылки на один объект
<г <= f >/ >= Операции "меньше", "меньше или равно", "больше", "больше или
равно", выполняемые над целыми числами
&&/ Ii Логические операции AND, OR. Для повышения эффективности
вычисления производятся по сокращенной схеме
! Логическое о т р и ц а н и е

if {логическое_выраэн:ение) операция
if {логическое_вырал€ение) операция! e l s e операция2
После ключевого слова i f в скобках задается логическое выражение ( b o o l e a n ) .
Если логическое выражение принимает значение t r u e , выполняется операция,
следующая за ним. Если значение логического выражения равно f a l s e , выполня­
ется операция, следующая за ключевым словом e l s e (если это слово присутствует
в составе оператора). Если в скобках после i f указано выражение, тип которого
отличен от b o o l e a n , компилятор выведет сообщение об ошибке. Этим Java отли­
чается от С и C++, где любое значение, не равное нулю, интерпретируется как
t r u e . В следующем примере возвращается наибольшее из дв}'х целых чисел:
/ / См. Math.max
public static int max2(int nl, int n2) {
if (nl >= n2)
return(nl);
else
return(n2);
}
В состав выражения i f - e l s e можно включать несколько операций, объединяя их
с помощью фигурных скобок. Н а п р и м е р :
8.3. Операторы, условные операторы и циклы 261

public static int inax2Verbose(int nl, int n2) {


if (nl >= n2) {
System.out.println(nl + " is larger.");
return(nl);
} else {
System.out.println(n2 + " is larger.");
return(n2);
}
}
Заметьте, что ключевое слово else всегда относится к последнему оператору if,
для которого else еще не было указано. Например:
public static int max3(int nl, int n2, int n3) {
if (nl >= n2)
if (nl >= n3)
return(nl);
else
return(n3);
else
if (n2 >= n3)
return(n2);
else
return(n3);
}
В этом примере для демонстрации вложенности операторов i f и принадлежности
e l s e используется отступ. Заметьте, что вложенный оператор i f рассматривается
как одно выражеР1ие, несмотря на то, что он занимает несколько строк, поэтому
фигурные скобки здесь не обязательны.
Для представления оператора i f в тексте программы применяются различные
типы отступа, примеры которых приведены ниже. В этой книге мы будем исполь­
зовать тип отступа, показанный первым.
/ / Первый тип отступа
p u b l i c SomeType someMethod(...) {
if {
statementl;
statementN
} else {
statementA;
statementZ;
}
}

/ / Второй тип отступа


p u b l i c SomeType someMethod(...)
{ if
{ statementl;
StatementN;
} else
{ StatementA;
262 Глава 8. Синтаксис Java

statementZ;
}
}
// Третий тип отступа
public SomeType someMethod(...)
{
if
{
statementl;
statementN;
}
else
{
statementA;
StatementZ;
}
}
Пи необходимости вы можете использовать в условном о п е р а т о р е несколько
сравнений, объединяя их с помощью логических операторов && (логическое И,
или операция AND) либо | | (логическое И Л И , или операция OR). Как и в языках
C/C++, эти операторы реализуют вычисления по "сокращенной схеме". Это зна­
чит, что результирующее значение возвращается в тот момент, когда появляется
достаточная и н ф о р м а ц и я для вычисления результата, даже если не все сравнения
были выполнены. Так, например, если два оператора сравнения объединены с по­
мощью && и выражение слева от && равно f a l s e , возвращается результат f a l s e ;
при этом оператор сравнения справа от && не выполняется. Аналогично, выраже­
ние, сформированное посредством оператора | |, возвращает t r u e , если его левая
часть равна t r u e . Сказанное иллюстрируется следующим примером:
public static int max3(int nl, int п2, int пЗ) {
if ((nl >= n2) && (nl >= пЗ))
return(nl);
else if ((n2 >= nl) && (n2 >= n3))
return(n2);
else
return(n3);
}
Логический оператор, обозначаемый с помощью единичного символа & или |, ука­
зывает на то, что должны быть вычислены оба операнда. Однако такие операции
применяются редко и их легко принять за операторы поразрядной логики.
логическое__выражение ? значение_1кеп: значение_е15е
Поскольку выражение i f не возвращает значение, Java предоставляет оператор, с
помощью которого вычисленное значение может быть присвоено переменной.
Приведенный ниже о п е р а т о р
if (someCondition)
someVar = valuel;
else
someVar = value2;
8.3. Операторы, условные операторы и циклы 263

можно сокращенно записать следующим образом:


someVar = (someCondition ? valuel : value2);
Такая форма записи допустима в любом случае, когда вам нужен условный опера­
тор, возвращающий значение. Так, рассмотренный выше метод тах2 может быть
представлен в следующем виде:
public static int max2Short(int nl, int n2) {
return((nl >= n2) ? nl : n2);
}
switch {выражение) { тело_оператора ]
Оператор s w i t c h представляет сокращенную запись выражения, используемого
для сравнения с набором целочисленных значений ( c h a r , b y t e , s h o r t , i n t ,
но не long). Выражение в скобках после ключевого слова s w i t c h возвращает це­
лочисленное значение, которое затем сравнивается с допустимыми значениями,
указанными посредством ключевого слова c a s e . При совпадении значений вы­
полняются операторы, следующие заданным и всеми остальными ключевыми сло­
вами c a s e . Приведенный ниже пример преобразует цифры из числового пред­
ставления в строковое.
p u b l i c s t a t i c S t r i n g number(int d i g i t ) {
switch(digit) {
case 0: r e t u r n " z e r o " ) ;
case 1: r e t u r n ("one");
case 2: r e t u r n ("two");
case 3: r e t u r n ( " t h r e e " ) ;
case 4: r e t u r n ( " f o u r " ) ;
case 5: r e t u r n • f i v e " ) ;
case 6: r e t u r n ( " s i x " ) ;
case 7: r e t u r n " s e v e n " ) ;
case 8: r e t u r n " e i g h t " ) ;
case 9: r e t u r n " n i n e " ) ;
default: return("Not a single d i g i t " ) ;

Далее в этой главе при обсуждении массивов мы рассмотрим более простой способ
преобразования целого числа в строковое представление. Ошибки, возникающие
при использовании оператора s w i t c h , чаще всего связаны с тем, что пользователь
не учитывает важную особенность данного оператора: при совпадении значения
выражения со значением c a s e выполняются не только операторы, следующие за
текущим c a s e , но и за всеми остальными c a s e . В некоторых ситуациях, подоб­
ных приведенному ниже примеру, такое поведение оператора s w i t c h позволяет
упростить программу.
switch(val) {
case testl:
case test2:
actionForTestland2();

}
264 Глава 8. Синтаксис Java

В большинстве случаев последовательность операторов, след)^ющих за c a s e , за­


канчивается оператором b r e a k или r e t u r n . В качестве примера перепишем
фрагмент кода, преобразующий числовое представление ц и ф р ы в строковое, сле­
дующим образом:
// Некорректно составленная программа, в которой
// не учитываются особенности поведения оператора switch.

public static String numberVerbose(int digit) {


String results-
switch (digit) {
case 0: System.out.println("zero") ;
result = "zero";
case 1: System.out.println("one");
result = "one";
case 2: System.out.println("two");
result = "two";
case 3: System.out.println("three");
result = "three";
case 4: System.out.println("four");
result = "four";
case 5: System.out.println("five");
result = "five";
case 6: System.out.println("six");
result = "six";
case 7: System.out.println("seven");
result = "seven";
case 8: System.out.println("eight");
result = "eight";
case 9: System.out.println("nine");
result = "nine";
default: System.out.println(
"Not a single digit");
result = "Not a single digit";
}
return(result)

Поскольку после выражения c a s e нет никаких операторов, прерывающих после­


довательность обработки, выполнение продолжается. Поэтому при вызове
n u m b e r V e r b o s e (5) мы получим следующий результат:
five
six
seven
eight
nine
Not a single digit
Ч т о б ы избежать этого, последовательность операторов, находящихся после каж­
дого c a s e , должна заканчиваться оператором b r e a k , как в следующем примере:
public static String numberVerboseFixed(int digit) {
String result;
switch(digit) {
case 0: System.out.println("zero");
8.3. Операторы, условные операторы и циклы 265

result = "zero";
break;
case 1: System.out.println("one");
result = "one";
break;

default: System.out.println("Not a single digit");


result = "Not a single digit";
}
return(result);
}

Циклы
в языке Java определены те же основные циклы, что и в C/C++: w h i l e , do и f o r .
Эти языковые конструкции перечислены в табл. 8.4 и детально описаны в данном
разделе. Кроме того, Java также поддерживает о п е р а т о р ы b r e a k и c o n t i n u e , кото­
рые используются соответственно для выхода из цикла и принудительного выполне­
ния следующей итерации.

Таблица 8.4. Операторы циклов

Оператор Стандартная форма


while w h i l e {условие_продолжения) {
тело_г1,икла;
}
do do {
тело^цикла;
] w h i l e {условие_продолжения) ;
for tor {ипициализаг1,ия; условие_продолжения; обновление) {
тело_г1икла;
}

О п е р а т о р while
О п е р а т о р w h i l e п р о в е р я е т логическое выражение, заданное в скобках, и, если
вырг1жение возвращает значение t r u e , выполняет тело цикла. Например, сле­
дующий метод выводит числа в интервале от О до max:
p u b l i c s t a t i c v o i d l i s t N u m s l ( i n t max) {
i n t i = 0;
w h i l e ( i <= max) {
System.out.println("Number: " + i ) ;
i++;
}
}

П р и вызове l i s t N u m s 1 ( 5 ) мы получим следующий результат:


0: zero
1: one
266 Глава 8. Синтаксис Java

2: two
3: three
4: four

Оператор do
Оператор do отличается от w h i l e тем, что проверка логического выражения про­
водится после выполнения тела цикла. Это означает, что тело цикла обязательно бу­
дет выполнено хотя бы один раз. Ниже приведен вариант рассмотренного выше ме­
тода, в котором использован оператор do. Результат вызова l i s t N u m s 2 (5) будет та­
ким же, как и раньше, но если вы вызовете l i s t N u m s 2 ( - 5 ) , то увидите сообщение
"Number : О". Для сравнения: при вызове l i s t N u m s l (-5) не будет выведено ни од­
ного значения. Причина заключается в том, что оператор do проверяет условие по­
сле выполнения цикла, в то время как оператор w h i l e делает это до выполнения
цикла. При вызове l i s t N u m b e r (-5) тело цикла не будет выполнено ни разу.
public static void listNums2(int max) {
int i = 0;
do {
System.out.println("Number: " + i ) ;
i++;
} while (i <= max); // Точка с запятой должна присутствовать
}
Как правило, большинство начинающих программистов делают одну и ту же
ошибку: не указывают точку с запятой после окончания оператора d o .

Ц и к л for
Для организации цикла с целочисленным счетчиком чаще всего используется опе­
р а т о р f o r . П р и выполнении цикла сначала выполняется инициализация счетчика.
Затем, то тех пор, пока логическое выражение возвращает значение t r u e , выпол­
няется тело цикла и изменяется значение счетчика. Н и ж е приведен код метода
l i s t N u m s 3 , который при передаче ему положительных значений дает тот же ре­
зультат, что и два предыдущих примера.
public static void listNums3(int max) {
for(int i=0; i<max; i++) {
System.out.println("Number: " + i ) ;
}
}
Java позволяет не указывать любое из трех (или все три) выражений в скобках по­
сле ключевого слова f o r . Если логическое выражение пропущено, считается, что
оно возвращает значение t r u e . Следующие два оператора эквивалентны:
for(;;) { / * т е л о цикла */ }
и
while(true) { /* тело цикла */ }
Такая форма оператора f o r применяется в тех случаях, когда условие завершения
цикла трудно представить в виде логического выражения. Вместо этого в тело
цикла помещается условный о п е р а т о р , содержащий r e t u r n либо b r e a k .
8.3. Операторы, условные операторы и циклы 267

Выразительные возможности циклов while, do и for сопоставимы, и любой цикл


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

Листинг8.1. DropBall.Java

/•* Моделирование процесса падения мяча с большой высоты


* (550 футов). До тех пор пока мяч не достигнет земли,
* программа каждую секунду выводит высоту, на которой находится мяч.
V
public class DropBall {
public static void main(String[] args) {
int time = 0;
double start = 550.0, drop = 0 . 0 ;
double height = starts-
while (height > 0) {
System.out.println("After " + time +
(time==l ? " second, " : " seconds,") +
"the ball is at " + height + " feet.");
time++;
drop = freeFall(time);
height = start - drop;
}
System.out.println("Before " + time + " seconds could " +
"expire, the ball hit the ground!");
}
/** Вычисление расстояния при свободном падении.

public static double freeFall (float time) {


// Ускорение свободного падения равно 32 фута / с'^2.
return(16.О * time * time); // 1/2 gt^2
268 Глава 8. Синтаксис Java

Листинг 8.2. Выходные данные DropBall

Prompt> Java DropBall


After 0 seconds,the ball is at 550.0 feet.
After 1 second, the ball is at 534.0 feet.
After 2 seconds,the ball is at 486.0 feet.
After 3 seconds,the ball is at 406.0 feet.
After 4 seconds,the ball is at 294.0 feet.
After 5 seconds,the ball is at 150.0 feet.
Before 6 seconds could expire, the ball hit the ground!

8.4. Класс Math


Класс Math предоставляет ряд методов для арифметических вычислений, не под­
держиваемых встроенными операторами. Все эти методы объявлены как s t a t i c , по­
этому для их вызова не обязательно создавать экземпляр класса Math.

Константы
public static Hnal d o u b l e E
Эта константа равна основанию натурального логарифма, т.е. 2.7182818284590452354.

public static final d o u b l e P I


Данная константа представляет отношение длины окружности к ее диаметру, т. е.
число 3.14159265358979323846.

Методы общего назначения


public static int abs(int n u m )
public static l o n g abs(long n u m )
public static float abs(float n u m )
public static d o u b l e a b s ( d o u b l e n u m )
Данные методы возвращают абсолютное значение указанного числа.

public static d o u b l e c e i l ( d o u b l e n u m )
public static d o u b l e floor(double num)
Метод c e i l возвращает d o u b l e - п р е д с т а в л е н и е наименьшего из целых чисел,
большего или равного указанному числу. Метод f l o o r возвращает d o u b l e -
представление наибольшего из целых чисел, не превышающих указанное число.

public static e x p ( d o u b l e n u m )
Данный метод возвращает е"""\
8.4. Класс Math 269

public static double IEEEremainder(double fl, double f2)


Данный метод возвращает остаток от деления f 1 на f 2 в соответствии со стандар­
том IEEE 754.

public static double log(double num)


Данный метод возвращает натуральный логарифм указанного числа. Java не под­
держивает вычисление логарифмов по другим основаниям (например, 10 или 2).
Для их определения можно использовать следующее соотношение:
1оды(п) = 1одь2(п) / 1одь2(Ь1)
p u b l i c s t a t i c double l o g ( d o u b l e num, double base) {
return(Math.log(num) / M a t h , l o g ( b a s e ) ) ;
}

public static int max(int numl, int num2)


public static long max(long numl, long num2)
public static float max(float numl, float num2)
public static double max(double numl, double num2)
public static int min(int numl, int num2)
public static long min(long numl, long num2)
public static float min(float numl, float num2)
public static double min(double numl, double num2)
Данные методы возвращают наибольшее (max) и наименьшее (min) из двух чисел.

public static double pow(double base, double exponent)


Метод pow возвращает base^'^^^"'^"'.

public static double random()


Данный метод возвращает случайное число в диапазоне от 0.0 (включительно) до
1.0 (это значение никогда не достигается). Дополнительные возможности по об­
работке случайных чисел реализуются посредством класса j ava . u t i l . Random.

public static double rint(double num)


public static int round(float num)
public static long round(double num)
Эти методы округляют число до ближайшего целого. Они отличаются типами воз­
вращаемых значений, а также порядком обработки числа ххх.5. Методы round
округляют данное число до большего значения, а метод r i n t — до ближайшего
четного числа, в соответствии со стандартом IEEE 754. Поведение метода r i n t
может оказаться необычным, однако оно препятствует накоплению ошибки при
с)т^1мировании большого количества округленных чисел.

public static double sqrt(double num)


Данный метод возвращает квадратный корень из num (для неотрицательных чи­
сел) и Double . NaN — для значения NaN и отрицательных чисел.
270 Глава 8. Синтаксис Java

Тригонометрические методы
public static double sin(double radians)
public static double cos(double radians)
public static double tan(double radians)
Указанные методы возвращают соответственно синус, косинус и тангенс указанно­
го числа, которое интерпретируется как угол в радианах.

public static double toDegrees(double radians)


public static double toRadians(double degrees)
Данные методы реализуют преобразование радиан в градусы и градусов — в радиа­
ны. Например, следующее выражение вычисляет синус угла, равного 60 градусам:
Math, sin (Math. toRadians (60.0) )

public static double acos(double val)


public static double asin(double val)
public static double atan(double val)
Эти методы позволяют вычислить арксинус, арккосинус и арктангенс указанного
числа. Результаты интерпретируются как значения в радианах.

Biglnteger И BigDecimal
Java поддерживает два числовых формата произвольной точности: Java.math.
B i g l n t e g e r и Java .math . BigDecimal. Эти классы содержат методы, позволяющие
выполнять сложение, умножение, деление, возведение в степень, находить наиболь­
ший общий делитель и производить многие другие операции над такими числами.
Подробно эти классы описаны в документации, основной же их характеристикой яв­
ляется тот факт, что они позволяют получить требуемый уровень точности. Так, на­
пример, в B i g l n t e g e r каждая цифра является значащей и такое понятие, как пере­
полнение, для этого класса отсутствует. В качестве примера в листинге 8.3 показано
использование B i g l n t e g e r для представления точного значения N ! (факториала N,
т.е. произведения N* (N-1) * (N-2) * . . . *1) для различных N. Результаты вычисле­
ний представлены в листинге 8.4.

Листинг 8.3. F a c t o r i a l . j a v a

import Java.math.Biglnteger;
/** Вычисление факториала с помощью B i g l n t e g e r . */
public class Factorial {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
f o r ( i n t i = l ; i<=256; i'^=2) {
S y s t e m . o u t . p r i n t l n ( i + "!=" + f a c t o r i a l ( i ) ) ;
}
}
8.5. Ввод и вывод 271

public static Biglnteger factorial(int n) {


if (n <= 1) {
return(new Biglnteger("1"));
} else {
Biglnteger bigN = new Biglnteger(String.valueOf(n));
return(bigN.multiply(factorial(n - 1) ) ) ;
}
}
}

Листинг 8.4. Выходные данные F a c t o r i a l

Prompt> Java Factorial


1!=1
2! =2
4 !=24
81=40320
161=20922789888000
32 1=2 6313083 6933 693530167218012160000000
64 1=12 688 693218588 41641034 3338 93351614 808028 6551617 4 54 51921988018 94 375
2147 042 304 00000000000000
128 1=3856204 823 625804 217 3567 7 0 6592 34 63 64 0 617 4 9310 95902235 9027 882 8 4 0327
637 34 0257 516554 35 60 68 616858 8 507 3 61534 0300518 33058 91634 7 592172 9322 624 98
8577 66114 9552 4 503 9357 7 60034 64 4 7 0927 92 4 7 692 4 955852800000000000000000000
00000000000
2 561=8 57 8177 7 534 28 4 2 654119082271681232 62 5157 7 8152027 94 856198 5 9 655 65037
72 694 5255314 7 58 9377 4 4 02 913 604 514 08 4 5037 58 8534 233 658 4 30 615719 6834 693 69
4 7 532228 9288 4 97 4 2 602567 9 637 3325 633 687 8 64 4 2 67 5207 62 67 94 560187 968 8 67 9
2114 330770207752 664 64 514 6470 918732 610083287 6325702818 9807736717814 5417
0250523018 6084 953190 6813 8257 4 81070252817 5594 594 7 6987034 66571273813 928 6
205234 7568082188 607 01203 6110831520 9350194 7 4 3710 910172 69 682 628 616062 63 6
624 350228 4 0 94 41914 084 24 61593 600000000000000000000000000000000000000000
0000000000000000000000

Внимание!
Следует отличать пакет java .math, содержащий классы Biglnteger
и BlgDeclmal, ОТ класса java. lang.Math. Класс Math используется
достаточно часто. Он содержит методы общего назначения, позво­
ляющие выполнять математические вычисления.

8.5. Ввод и вывод


в данном разделе рассматриваются простейшие средства, предназначенные для
организации ввода-вывода.
272 Глава 8. Синтаксис Java

Вывод в стандартный выходной поток


в данной книге вам уже встречались вызовы метода S y s t e m . o u t . p r i n t l n . Зная
соглашения об именовании Java, легко догадаться, что out — это переменная класса
System, а в классе, которому соответствует эта переменная ( P r i n t S t r e a m ) , опреде­
лен метод p r i n t l n . Помимо p r i n t l n класс P r i n t S t r e a m содержит также метод
p r i n t . Этот метод выполняет те же действия, что p r i n t l n , но не выводит после ука­
занных данных символ перевода строки. При вызове методу p r i n t l n (или p r i n t )
передается один параметр произвольного типа. Простые типы преобразуются в
строковое значение путем вызова метода S t r i n g . valueOf, а объекты, отличные от
объекта S t r i n g , преобразуются в строку с помощью метода t o S t r i n g , содержащего­
ся в составе этого объекта. При формировании строк может использоваться оператор
конкатенации -ь. Пример применения метода p r i n t l n приведен ниже.
System.out.println(2 + 'В' + " || ! " + new I n t e g e r ( 2 ) + "В");

Кроме p r i n t l n и p r i n t класс P r i n t S t r e a m содержит метод f l u s h . Этот метод


применим тогда, когда надо убедиться, что выходные данные были переданы в поток,
а не остались в буфере. Подробную информацию о классе J a v a . i o . P r i n t S t r e a m вы
найдете в электронной документации по API.
Следует заметить, что средства форматирования выходных данных в Java несколь­
ко сложнее, чем функции С p r i n t f и s p r i n t f . Для управления форматированием
числовых данных предназначен класс J a v a . t e x t . DecimalFormat. При этом созда­
ются объекты, описывающие формат чисел. Для преобразования чисел в форматиро­
ванные строки используется метод format. Пример форматирования чисел приведен
в листинге 8.5, а результаты показаны в листинге 8.6.

Листинг 8.5. NumFormat. java

import java.text.*;
/** Форматирование чисел посредством DecimalFormat. */

public class NumFormat {


public static void main (String[] args) {
DecimalFormat science = new DecimalFormat("0.OOOEO");
DecimalFormat plain = new DecimalFormat("0.0000");

for(double d=100.0; d<140.0; d*=1.10) {


System.out.println("Scientific: " + science.format(d)
" and Plain: " + plain.format(d));
}
}
8.5. Ввод и вывод 273

Л и с т и н г 8 . 6 . Выходные д а н н ы е NumFormat

Prompt> Java NumFormat


Scientific: 1.000E2 and Plain: 100.0000
Scientific: 1.100E2 and Plain: 110.0000
Scientific: 1.210E2 and Plain: 121.0000
Scientific: 1.331E2 and Plain: 133.1000

В приведенном примере конструктору D e c i m a l F o r m a t передается объект


S t r i n g , определяющий шаблон для отображения числа. В шаблоне указывается чис­
ло ц и ф р , предназначенных для отображения, и разделители. В табл. 8.5 перечислены
символы, которые могупг быть использованы в шаблонах. Так, например, строка
, ### . О задает отображение одной ц и ф р ы после десятичной точки, с использование
запятой для разделения триад десятичных ц и ф р . В этом случае число 23767.82 будет
отображено как 23,767.8, а число 0.43 — как .4 (нуль перед запятой не выводится).
Класс D e c i m a l F o r m a t обеспечивает лишь минимальный контроль при выводе чи­
сел. Если использование D e c i m a l F o r m a t по каким-то причинам вызывает у вас труд­
ности, воспользуйтесь заменителями p r i n t f . Один из лучших описан в документе
h t t p : //www. acme . c o m / j a v a / s o f t w a r e / A c m e . Fmt. h t m l .
Выходные данные, переданные S y s t e m , o u t , могут быть перенаправлены в другой
поток. Перенаправлением удобно пользоваться в тех случаях, когда вам надо сохра­
нить сообщения, выводимые на консоль, в файле протокола. Для этого достаточно
вызвать метод S y s t e m , s e t Out, указав объект P r i n t S t r e a m , которому вы хотите на­
править выходные данные.

Таблица 8.5. Символы, используемые для форматирования

Символ Описание
О Резервирует место для ц и ф р ы
# Резервирует место для ц и ф р ы . Ведущий или завершающий нуль, она не
выводится
Расположение десятичной точки
/ Указывает позицию, в которой должна отображаться запятая
Знак "минус"
Е Указывает позицию, в которой мантисса должна отделяться от степени
% Значение умножается на 100 и отображается как величина в процентах

Вывод в стандартный поток ошибок


Помимо S y s t e m , o u t в Java существует переменная S y s t e m , e r r , с помощью кото­
рой хможно организовать вывод в поток ошибок. Данный поток также реализуется с
помощью класса P r i n t S t r e a m и используется аналогично S y s t e m , o u t . Для того
чтобы направить вывод в данный поток, надо вызвать метод S y s t e m . s e t E r г .
274 Глава 8. Синтаксис Java

Чтение из стандартного входного потока


Ч т е н и е из стандартного входного потока — не типичная операция для программ,
регшизованных на языке Java. Как правило, программы, не предоставляющие графи­
ческий пользовательский и н т е р ф е й с , получают входные данные посредством ко­
мандной строки. Среди средств графического интерфейса для этой цели предназна­
чены поля ввода текста, л и н е й н ы е регуляторы и другие управляющие элементы. Если
же необходимо организовать ввод посредством стандартного входного потока, вам
надо прежде всего связать S y s t e m , i n с Buf f e r e d R e a d e r следующим образом:
BufferedReader keyboard =
new B u f f e r e d R e a d e r (
new I n p u t S t r e a i u R e a d e r ( S y s t e m , i n ) ) ;
Затем для получения строки символов следует вызвать метод r e a d L i n e класса
BufferedReader:
String line = keyboard.readLine();

8.6. Выполнение программ, отличных от Java


Несмотря на то что из соображений безопасности аплетам запрещено вызывать
системные программы, Java-приложениям (независимым Java-программам) эта опера­
ция доступна. Для запуска программы на локальной машине надо выполнить следую­
щие действия.
1. Получить специальный объект Runtime. Для этого используется статический
метод g e t R u n t i m e класса R u n t i m e .
Runtime r t = Runtime.getRuntime();
2. Запустить программу на выполнение. Для этого применяется метод e x e c , ко­
т о р ы й возвращает объект P r o c e s s .
Process proc = rt.exec("someProgram");
Данный метод запускает программу, но не ожидает ее завершения или вывода
результатов. Заметьте, что метод e x e c не использует для поиска исполняемых
программ переменную окружения PATH, поэтому вам необходимо указать пол­
ный путь. Кроме того, метод e x e c не запускает оболочку и такие символы, как
">" и " I ", не распознаются. Заметьте также, что в классе R u n t i m e определен
вариант метода e x e c , которому при вызове передается массив объектов
S t r i n g , содержимое которого обрабатывается в программе как набор пара­
метров командной строки. Н а п р и м е р :
String[] args = { "-1", "*.java" };
rt.exec{"Is", args); // Вывод содержимого каталога.
3. Олсидать завершения программы (данное действие необязательно). Метод
e x e c возвращает управление сразу после запуска программы, независимо от то­
го, сколько времени нужно программе для выполнения. Это позволяет продол­
жить обработку данных, не дожидаясь завершения вызываемой программы.
Однако некоторых разработчиков такое поведение метода e x e c не устраивает.
8 . 6 . Выполнение п р о г р а м м , отличных от Java 275

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


после завершения вызываемой программы, надо после запуска процесса вы­
звать метод w a i t F o r .
proc.waitFor()/
Предположим, например, что вы хотите вызвать из вашей программы компи­
лятор j a v a c , а затем запустить полученный файл с помощью интерпретатора
J a v a . Очевидно, что запуск программы должен осуществляться уже после за­
вершения компиляции. Заметьте, что метод w a i t F o r не поддерживает вывод
результатов выполнения программы, он лишь ожидает ее завершения.
4. Обработать результаты выполнения программы (данное действие необяза­
тельно). Возможно, что для выполнения поставленной задачи вам надо не
только дождаться завершения работы программы, но и обработать выводимые
ею данные. Так, например, вызывать программу, отображающую содержимое
каталога, бессмысленно, если вы не можете получить ее выходные данные.
Можно получить результаты выполнения программы, связав с процессом объ­
ект Buf f e r e d R e a d e r , как это делается в следующем примере:
try {
BufferedReader buffer =
new BufferedReader(
new InputStreamReader(proc.getInputStream()));
String s = null;
while ((s = buffer.readLine()) != null) {
System.out.println("Output: " + s);
}
buffer.close()/
} catch(Exception e) {
/* Игнорировать ошибки чтения */
}
В листинге 8.7 все четыре этапа вызова внешней программы реализованы в рамках
одного класса E x e c , содержащего методы e x e c , e x e c P r i n t и e x e c W a i t . Код, иллю­
стрирующий использование данного класса, содержится в листинге 8.8.
Запуск внешней программы и взаимодействие с ней связаны с определенным рис­
ком, поскольку при этом задействуются средства операционной системы. Так, напри­
мер, не исключена "тупиковая ситуация" при блокировке, которая может привести к
зависанию системы.

Внимание!

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


данные во входном и выходном потоках не будут обработаны долж­
ным образом, возможно возникновение "тупиковых ситуаций".
276 Глава 8. Синтаксис Java

Листинге.?. Exec. Java

import java.io.*;
/** Класс, который упрощает запуск внешних процессов из
^ приложений. Программы можно запускать тремя способами:
* <0L>
* <LI><B>exec</B>: управление возвращается сразу после
* вызова, даже если выполнение команды еще продолжается.
* Это удобно для таких задач, как вывод файла на печать.
* <LI><B>execWait</B>: управление не возвращается до тех
* пор, пока выполнение команды не будет завершено.
* Это удобно для последовательности команд, когда
* следующая команда зависит от результатов предыдущей
* (например, <CODE>javac</CODE> и <CODE>java</CODE>).
* <LI><B>execPrint</B>: команда выполняется, и выводятся
* результаты. Это удобно для команды UNIX
* <CODE>ls</CODE>.
* </0L>
* Заметьте, что переменная PATH не используется, поэтому вы
* должны задать <В>полный</В> путь. Кроме того, команды
* оболочки не поддерживаются. Например, в системе UNIX
* три приведенных примера будут иметь вид
* <0L>
* <LI><PRE>Exec.exec("/usr/ucb/lpr Some-File");</PRE>
* <LI><PRE>Exec.execWait("/usr/local/bin/javac Foo.java");
* Exec.execWait("/usr/local/bin/Java Foo");</PRE>
* <LI><PRE>Exec.execPrint("/usr/bin/ls -al");</PRE>
* </0L>

public class Exec {

private static boolean verbose = true;

/** Определяет, должен ли класс Exec выводить информацию


* о выполняемых командах и сообщения об ошибках.
'^ По умолчанию предполагается значение true.

* @param verboseFlag true: сообщения выводятся, false: не выводятся.

public static void setVerbose(boolean verboseFlag) {


verbose = verboseFlag;
}
/** Должны ли методы класса Exec выводить сообщения о состоянии? */

public static boolean getVerboseO {


return(verbose);

/** Запуск процесса выполнения команды. Управление


* возвращается немедленно, даже если выполнение
* команды продолжается.
8.6. Выполнение программ, отличных от Java 277

* 0param command <В>Полный</В> путь к соответствующей


* программе. Команды (например, "cd") и специальные
* символы (например, ">") оболочки не поддерживаются.
* @return false, если при выполнении команды должна
* возникнуть проблема, но поскольку управление
* возвращается немедленно, она еще не выявлена.
* В противном случае возвращается значение true.
V
public static boolean exec(String command) {
return(exec(command, false, false));
}
/*"^ Запуск процесса выполнения команды. Управление
* возвращается после того, как выполнение команды окончено.

* @param command <В>Полный</В> путь к соответствующей


* программе. Команды и специальные символы оболочки
* не поддерживаются.
* @return false, если при выполнении команды возникает
* проблема. (Об этом сигнализирует исключение, либо
'^ ненулевое значение, возвращаемое дочерним процессом. )
* В противном случае возвращается значение true.

public static boolean execWait(String command) {


return(exec(command, false, true));
}
/** Запуск процесса выполнения команды. При этом выводятся
* выходные данные, сгенерированные при выполнении команды.

* @param command <В>Полный</В> путь к соответствующей
* программе. Команды и специальные символы оболочки
* не поддерживаются.
* (Эreturn false, если при выполнении команды возникает
* проблема. (Об этом сигнализирует исключение, либо
* ненулевое значение, возвращаемое дочерним процессом.)
* В противном случае возвращается значение true.
V
public static boolean execPrint(String command) {
return(exec(command, true, false));

/** Создание объекта Process посредством Runtime.getRuntime.exec()


* В зависимости от значений флагов, может быть вызван
* метод waitFor, предотвращающий продолжение работы до
* завершения процесса, и открыт входной поток для
* получения результатов выполнения команды.
V
private static boolean exec(String command,
boolean printResults,
boolean wait) {
278 Глава 8. Синтаксис Java

if (verbose) {
printSeparator();
System.out.println("Executing '" + command + " ' . " ) ;
}
try {
// Запуск команды; немедленная передача управления.
Process р = Runtime.getRuntime().exec(command);

// Вывод выходных значений. Поскольку необходимо


// убедиться, что все значения выведены, необходимо
// дождаться окончания выполнения команды,
if(printResults) {
BufferedReader buffer = new BufferedReader(
new InputStreamReader(p.getlnputstream()));
String s = null;
try {
while ((s = buffer.readLine()) != null) {
System.out.println("Output: " + s ) ;
}
buffer.close() ;
if (p.exitValue0 != 0) {
if (verbose) {
printError(command + " -- p.exitValue() != 0")
}
return(false);
}
} catch (Exception e) {
// Ошибки чтения игнорируются. Подобная ошибка
// означает, что процесс завершен.
}
// Если результаты не выводятся, надо вызвать waitFor,
// чтобы приостановить работу до завершения процесса.

} else if (wait) {
try {
System.out.println ( " " ) ;
int returnVal = p.waitFor();
if (returnVal != 0) {
if (verbose) {
printError(command);
}
return(false);
}
} catch (Exception e) {
if (verbose) {
printError(command, e ) ;
}
return(false);
}
}
} catch (Exception e) {
if (verbose) {
printError(command, e ) ;
}
return(false);
8.6. Выполнение программ, отличных от Java 279

}
return(true);
}

private static void printError(String command,


Exception e) {
System.out.println("Error doing exec(" + command + " ) : " +
e.getMessage());
System.out.println("Did you specify the full " +
"pathname?");

private static void printError(String command) {


System.out.println("Error executing '" + command + "'.")

private static void printSeparator() {


System.out.println

В листинге 8.8 показано использование класса Exec в системе UNIX, а результаты


приведены в листинге 8.9.

Листинг8.8. ExecTest.Java

/** Использование класса Exec. */

public class ExecTest {


public static void main(String[] args) {
// Заметьте, что завершающий символ "&" отсутствует,
// так как специальные символы оболочки не поддерживаются.
// Метод exec не дожидается завершения команды,
// поэтому с его помощью можно запускать даже броузер
// Netscape.
Exec.exec("/usг/local/bin/netscape");

// Выполнение команды, вывод результатов.


Exec.execPrint("/usг/bin/Is");
Exec.execPrint("/usr/bin/cat Test.Java");

// Результаты не выводятся. Управление не возвращается


/ / д о завершения команды.
Exec.execWait("/usr/javal.3/bin/javac Test.Java");

// Файл Test.class должен существовать.


Exec.execPrint("/usr/bin/ls");
}
}
280 Глава 8. Синтаксис Java

Листинг 8.9. Выходные данные ExecTest

Unix> Java ExecTest

Executing '/usr/local/bin/netscape'.

Executing '/usr/bin/ls'.
Output: Exec.class
Output: Exec.Java
Output: ExecTest.class
Output: ExecTest.Java
Output: Test.Java

Executing '/usr/bin/cat Test.Java'.


Output: public class Test {
Output: boolean flag;
Output: }

Executing '/usr/javal.3/bin/javac Test.Java'

Executing '/usr/bin/ls'.
Output: Exec.class
Output: Exec.Java
Output: ExecTest.class
Output: ExecTest.Java
Output: Test.class
Output: Test.Java

8.7. Ссылки
Значения, которые являются объектами, т.е. не относящиеся к простым типам
(экземпляры классов или массивы), называют ссылочными значениями, или просто
ссылками. П р и обсуждении программ на Java принято говорить, что значение той или
иной переменной представляет собой объект. Поскольку в Java не существует опера­
ций, связанных с прямым присвоением значений указателям, бытует мнение, что в
Java указатели отсутствуют. Это ошибка! ^Вг^ значения, не принадлежащие к простым
типам, реализуются только с помощью указателей. Программистам, имеющим опыт в
разработке программ на С, легче будет понять реальное положение вещей, если ска­
зать, что значения переменных, не относящихся к простым типам, указывают на объ­
екты. В Java не существует отличия между переменной, codej)Maiu,eu объект, и перемен­
ной, указывающей на объект.
Если вы еще не знакомы с указателями, можете себе представить их следующим
образом. Чтобы повысить эффективность работы программ, Java не выполняет копи­
рование больших и сложных объектов каждый раз, когда они передаются от одного
метода другому. Вместо объектов передаются данные, описывающие их расположе­
ние. Если вы работали с указателями на других языках программирования, вам надо
запомнить, что Java не позволяет непосредственно задавать значения указателей. Ес­
ли переменная ссылается на объект, невозможно изменить ее так, чтобы она ссыла­
лась на объект другого типа.
8.7. Ссылки 281

Пример использования ссылок приведен в листинге 8.10, а выходные данные— в


листинге 8.11.

Листинг В. 10. ReferenceTest.Java

import Java . awt. Points-


public class ReferenceTest {
public static void main(String[] args) {
Point pi = new Point(1, 2 ) ; // Переменная pi ссылается
/ / н а объект Point.
Point p2 = pi; // В переменной p2 хранится еще одна
// ссылка на тот же объект Point,
print("р1", р1); // (1, 2)
print("р2", р2); // (1, 2)
triple(р2); // Переменная р2 не изменяется
print("р2", р2); // (1, 2)
р2 = triple(р2); // Переменная р2 ссылается
/ / н а новый объект Point
print("р2", р2); // (3, 6)
print("р1", р1); // р1 не изменяется: (1, 2)

public static Point triple(Point p) {


p = new Point(p.X * 3, p.у * 3 ) ; // Перенаправление p
return(p);

public static void print(String name. Point p) {


System.out.println("Point " + name + "= (" +
p.X + ", " + p.у + " ) . " ) ;

Листинг 8.11. Выходные данные ReferenceTest

Prompt> Java ReferenceTest


Point pl = (1, 2)
Point p2= (1, 2)
Point p2 = (1, 2)
Point p2 = (3, 6)
Point pl= (1, 2)

Заметьте, что изменения переменной р в методе triple не изменяют переменную


р2, которая была указана при вызове метода. После выполнения операции присвое­
ния р ссылается на новый объект, а значение переменной р2 остается прежним. Для
того чтобы р2 ссылалась на новый объект, мы присваиваем ей значение, возвращае­
мое методом triple. Несмотря на то что в теле метода невозможно изменить содер­
жимое переменной (т.е. присвоить ей ссылк)^ на объект), можно, тем не менее, изме­
нить поля объекта (при наличии доступа к ним). Код, изменяющий поля объекта, по­
казан в листинге 8.12, а результаты его выполнения — в листинге 8.13.
282 Глава 8. Синтаксис Java

Листинг 8.12.ModificationTest.Java

import Java.awt.Point;
public class ModificationTest extends.ReferenceTest {
public static void main(String[] args) {
Point pi = new Point(1, 2 ) ; // Переменная pi ссылается
/ / н а объект Point.
Point p2 = pi; // В переменной p2 хранится еще одна
// ссылка на тот же объект Point,
print("р1", р1); // (1, 2)
print("р2", р2); // (1, 2)
munge(p2); // Модификация полей объекта Point.
printC'pl", pi) ; // (5, 10)
print("р2", p2); // (5, 10)
}
public static void munge(Point p) {
p.x = 5;
p.У = 10;
}
}

Листинг 8.13. Выходные данные Modif icationTest

Prompt> Java ModificationTest


Point pl= (1, 2) .
Point p2= (1, 2 ) .
Point pl= (5, 10).
Point p2= (5, 10).

Соглашения no передаче параметров


Если вы уже знакомы с терминами "передача параметров по значению" и
"передача параметров по ссылке", то, возможно^ затруднитесь определить, по какой
же схеме работает Java. Это не может быть передачей параметров по ссылке, по­
скольку изменения переменной р в теле метода t r i p l e никак не отражаются на пе­
ременной р 2 , передаваемой методу в качестве параметра. Однако действия с пара­
метрами не укладываются и в схему передачи по значению, поскольку munge демон­
стрирует, что методу доступен сам объект, а не его копия. Поэтому не задумывайтесь
о терминологии, а запомните следующее правило.

На з а м е т к у

Если переменная передается иауа-методу, в теле метода невоз- 4


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

Если вы полностью поняли это правило, то, наверное, скажете, что Java передает
параметры по значению, но сами эти значения являются ссылками.
8.7. Ссылки 283

Оператор instanceof
Оператор i n s t a n c e o f возвращает значение t r u e , если операнд в его левой части
соответствует классу, или интерфейсу, указанному в правой части. Соответствие в
данном случае означает, что объект является экземпляром указанного класса или его
подкласса, либо, если вместо класса задан интерфейс, объект является экземпляром
класса, реализующего этот интерфейс, либо его подкласса. Например:
if (item instanceof Breakable) {
add(item, chinaCabinet);
}
При использовании оператора instanceof возникает одна проблема: при напи­
сании кода необходимо знать имя класса, на принадлежность к которому выполняется
проверка. Для того чтобы подобная задача могла быть решена в любой ситуации, в
Java 1.1 была реализована динамическая версия проверки на принадлежность к клас­
су. Она состоит в вызове метода islnstance объекта Java. lang.Class и передаче
ему требуемого объекта. Пример использования метода islnstance приведен в лис­
тинге 8.14, а результаты выполнения кода — в листинге 8.15.

Листинг 8.14.Instanceof.Java

interface Barking {}

class Mammal {}

class Canine extends Mammal {}

class Dog extends Canine implements Barking {}

class Retriever extends Dog { }

public class Instanceof {


public static void main(String[] args) {
Canine wolf = new Canine();
Retriever rover = new Retriever();
System.out.println("Testing instanceof:");
report(wolf, "wolf");
System.out.println() ;
report(rover, "rover");

System.out.println("\nTesting islnstance:");
Class barkingClass = Barking.class;
Class dogClass = Dog.class;
Class retrieverClass = Retriever.class;
System.out.println(" Does a retriever bark? " +
barkingClass.islnstance(rover));
System.out.println(" Is a retriever a dog? " +
dogClass.islnstance(rover));
System.out.println(" Is a dog necessarily a retriever? " +
retrieverClass.islnstance(new Dog ()));
284 Глава 8. Синтаксис Java

public static void report(Object object, String name) {


System.out.println(" " + name + " is a mammal: " +
(object instanceof Mammal));
System.out.println(" " + name + " is a canine: " +
(object instanceof Canine));
System.out.println(" " + name + " is a dog: " +
(object instanceof Dog));
System.out.println(" " + name + " is a retriever: " -
(object instanceof Retriever));

Листинг 8.15. Выходные данные instanceOf

prompt> Java InstanceOf


Testing instanceof:
wolf is a mammal: true
wolf is a canine: true
wolf is a dog: false
wolf is a retriever: false
rover is a mammal: true
rover is a canine: true
rover is a dog: true
rover is a retriever: true

Testing islnstance:
Does a retriever bark? true
Is a retriever a dog? true
Is a dog necessarily a retriever? false

8.8. Работа со строками


В Java строки являются реальными объектами — экземплярами класса j a v a . l a n g .
S t r i n g . Поскольк)^ эти объекты используются чрезвычайно часто, для их создания и
инициализации можно использовать сокращенную запись, указав значение в двойных
кавычках, например:
String si = "This is a String";
Известные вам общие правила создания экземпляров объектов также применимы
к строкам.
S t r i n g s2 = new S t r i n g ( " T h i s is a String too");
Класс S t r i n g имеет одну особенность, отличающую его от других классов, — стро­
ка не модифицируется. Единожды созданный объект S t r i n g существует в неизмен­
ном виде до тех пор, пока он не будет удален системой "сбора мусора". Дер^ствитель-
но, скажете вы, в классе S t r i n g отсутствует метод C h a r a c t e r A t , но мне неоднократ-
8.8. Работа со строками 285

но встречалась операция конкатенации строк. Рассмотрим пример, в котором для


конкатенации строк используется символ "+":
String t e s t = "foo" + "bar"; // "foobar"
Однако в этом примере создаются три объекта S t r i n g . В одном из них содержит­
ся строка "foo", в другом— строка " b a r " , а в третьем— конкатенация этих строк —
" f o o b a r " . Эта особенность станет более очевидной, если переписать представлен­
ный выше код следующим образом:
S t r i n g foo = " f o o " ;
String bar = "bar";
S t r i n g t e s t = foo + b a r ;
П р и создании третьей строки не модифицируется ни объект f o o , ни объект b a r .
Такая особенность строк очень удобна для разработчика: вы можете передать строку
любому методу, не опасаясь того, что в теле метода в нее будут внесены изменения. С
другой стороны, если строка не изменяется, то при выполнении конкатенации стро­
ки должны копироваться. Ч т о б ы избежать связанного с копированием снижения
производительности, в Java предусмотрен класс S t r i n g B u f f е г , содержимое которо­
го может быть изменено.
Заметьте, что + — это единственный переопределяемый оператор Java. Он действует
по-разному в зависимости от того, применяется ли он к строкам или к числовым значе­
ниям. При разработке программ вы не можете определить собственный оператор либо
переопределить имеющийся. Важной особенностью S t r i n g является тот факт, что
этот класс определен как f i n a l , поэтому подкласс S t r i n g создать невозможно.

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


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

public char charAt(int i n d e x )


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

public int c o m p a r e T o ( S t r i n g c o m p a r i s o n )
public int c o m p a r e T o ( O b j e c t object)
Метод c o m p a r e T o сравнивает текущую строку с указанной строкой. Сравнение
осуществляется посимвольно с учетом порядка следования символов в Unicode.
Если строки эквиваленты (содержат одинаковые символы), метод возвращает зна­
чение 0. Отрицательное значение указывает на то, что текущая строка лексико­
графически меньше, чем указанная строка. Возвращаемая положительная величи­
на означает обратное. Конкретное число либо равно разности межд)^ Unicode-
значениями первых несовпадающих символов, либо, если первая строка полно­
стью содержится в начале второй строки, — разности длин строк. Для того чтобы
обеспечить соответствие интерфейсу C o m p a r a b l e , в Java 1.2 был добавлен вари­
ант метода c o m p a r e T o , которому в качестве параметра передается объект Ob j e c t .
Он действует так же, как и метод c o m p a r e T o ( S t r i n g c o m p a r i s o n ) , но если па­
раметр не является строкой, генерируется исключение C l a s s C a s t E x c e p t i o n .
286 Глава 8 . Синтаксис Java

public String concat(String suffix)


Метод c o n c a t выполняет конкатенацию двух строк, формируя новый объект
S t r i n g . Два приведенных ниже выражения определяют одну и ту же операцию.
String result = someString.concat(someOtherString);
S t r i n g r e s u l t = someString + someOtherString;.
П р и выполнении этих выражений не изменяется ни s o m e S t r i n g , ни
s o m e O t h e r S t r i n g ; вместо этого создается новый объект S t r i n g .

public static String copyValueOf(char[ ] characters)


public static String copyValueOf(char[ ] data, int s t a r t l n d e x , int count)
Данные методы преобразуют массивы символов в строки.

public b o o l e a n endsWith(String suffix)


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

public b o o l e a n equals(Object c o m p a r i s o n )
Если объект c o m p a r i s o n не является строкой, метод e q u a l s возвращает значение
f a l s e . В противном случае выполняется посимвольное сравнение двух строк. Таким
образом, для сравнения строк должен всегда использоваться метод e q u a l s , а не
оператор ==. Как показано в примере, содержащемся в листинге 8.17, проверка с
помощью оператора == дает отрицательный результат, а проверка посредством ме­
тода e q u a l s — положительный. Заметьте также, что различные экземпляры строк,
заданных с помощью литерала, не обязательно будут распознаны оператором == как
равные, потому что компилятор может по-разному преобразовать эти константы.

Внимание!

Различные объекты string, содержащие одинаковые последова­


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

public b o o l e a n equalsIgnoreCase(String c o m p a r i s o n )
Данный метод осуществляет посимвольное сравнение строк без учета регистра
символов.

public byte[ ] getBytesO


public byte[ ] getBytes(String e n c o d i n g )
Данный метод преобразует строк)^ в массив символов.

public void getChars(int sourceStart, int s o u r c e E n d , char[ ] d e s t i n a t i o n ,


int destinationStart)
Данный метод копирует в указанную часть массива символы от s o u r c e S t a r t
(включая данную позицию) до последнего символа, предшествующего s o u r c e E n d .
8.8. Работа со строками 287

public int indexOf(int character)


public int indexOf(int character, int startlndex)
public int indexOf(String substring)
public int indexOf(String substring, int startlndex)
Данные методы возвращают индекс первого вхождения указанного символа или
указанной подстроки.

public native String intern()


Метод i n t e r n возвращает каноническое представление объекта S t r i n g , содер­
жащее те же самые символы, что и текущая строка. Если две строки распознаются
методом e q u a l s как равные, они будут гарантированно распознаваться операто­
ром == как равные.

public int lastIndexOf(int character)


public int lastIndexOf(int character, int startlndex)
public int lastIndexOf(String substring)
public int lastIndexOf(String substring, int startlndex)
Данные методы возвращают индекс последнего вхождения указанного символа
или строки.

public int length()


Данный метод возвращает длину строки. Заметьте, что это метод, а не переменная
экземпляра. Поэтому для определения длины строки вызывайте l e n g t h ()
i n t len = someString. l e n g t h О ; / / lengthO
a для определения длины массива используйте следующее выражение:
i n t len = someArray.length; / / Скобки не указываются

public boolean regionMatches(int startlndexl,


String string2, int startlndex2, int count)
public boolean regionMatches(boolean ignoreCase, int startlndexl,
String string2, int startlndex2, int count)
Данные методы выполняют проверку двух подстрок. При проверке может учиты­
ваться, а может не учитываться регистр символов.

public String replace(char oldChar, char newChar)


Метод r e p l a c e возвращает новый объект S t r i n g , являющийся результатом заме­
ны всех вхождений oldChar символами newChar. Исходная строка остается неиз­
менной.
288 Глава 8. Синтаксис Java

public b o o l e a n startsWith(String prefix)


public b o o l e a n startsWith(String prefix, int s t a r t l n d e x )
Данные методы проверяют, является ли указанная строка началом другой.

public String substring(int s t a r t l n d e x , int e n d l n d e x )


public String substring(int startlndex)
Каждый из указанных методов возвращает подстроку, содержащуюся в заданном
диапазоне. Если конечный индекс не приведен, считается, что окончание под­
строки совпадает с окончанием исходной строки.

public char[ ] toCharArray()


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

public String toLowerCase()


public String toLowerCase(Locale locale)
public String t o U p p e r C a s e O
public String t o U p p e r C a s e ( L o c a l e locale)
Указанные методы преобразуют символы, содержащиеся в составе строки, в верх­
ний или нижний регистр, используя для этого правила национальных кодировок.

public String trim()


Метод t r i m возвращает новую строку, в которой удалены ведущий и завершающие
пробелы, а также управляющие символы. Исходный объект S t r i n g не изменяется.

public static String valueOf(boolean b)


public static String valueOf(char c)
public static String valueOf(char[ ] data)
public static String valueOf(char[ ] data, int s t a r t l n d e x , int count)
public static String v a l u e O f ( d o u b l e d)
public static String valueOf(float f)
public static String valueOf(int i)
public static String valueOf(long 1)
Указанные статические методы преобразуют соответствующие простые типы в
объекты S t r i n g .

public static String valueOf(Object o)


Данный статический метод использует метод t o S t r i n g объекта для генерации
строки.
В листинге 8.16 приведен пример, демонстрирующий использование методов
класса S t r i n g . Результаты показаны в листинге 8.17.
8.8. Работа со строками 289

Листинг 8.16.StringTest.Java

public class StringTest {


public static void main (String[] args)
String str = "";
if (args.length > 0) {
str = args[0];
}
if (str.length()>8)
System.out.println "String is \"" + str + " \ " \ n " ) ;
System.out.println " charAtO) " +
str.charAt(3));
System.out.println " compareTo(Moscow) " +
str.compareTo("Moscow"));
System.out.println " concat(SuFFiX) " +
str.concat("SuFFiX"));
System.out.println " endsWith(hic) " +
str.endsWith("hic"));
System.out.println " == Geographic " +
(str == "Geographic"));
System.out.println " equals(geographic) " +
str.equals("geographic"));
System.out.println " equalsIgnoreCase(geographic) " +
str.equalsIgnoreCase("geographic"));
System.out.println " indexOf('o')
str.indexOf('o'));
System.out.println " indexOf ('14 5)
Str.indexOf ( 'i\5) ) ;
System.out.println " indexOf ('o\5)
Str.indexOf('o',5));
System.out.println " indexOf(rap) +
str.indexOf("rap"));
System.out.println " indexOf(rap, 5) +
Str.indexOf("rap", 5));
System.out.println " lastlndexOf('o') +
str.lastlndexOf('o*));
System.out.println " lastlndexOf ('14 5) +
Str.lastlndexOf('i',5));
System.out.println " lastlndexOf('o',5) 4-
Str.lastlndexOf('o',5));
System.out.println lastlndexOf(rap) +
str.lastlndexOf("rap"));
System.out.println lastlndexOf(rap, 5) +
Str.lastlndexOf("rap", 5));
System.out.println " length 0 +
str.length());
System.out.println " replace('c','k') +
str.replace('с *, 'к'));
System.out.println " startsWith(eog,1) +
str.startsWith("eog",1));
System.out.println " startsWith(eog) +
str.startsWith("eog"));
System.out.println " substring(3) +
str.substring(3));
System.out.println " substring(3,8) +
290 Глава 8. Синтаксис Java

str.substring(3,8)) ;
System.out.println(" toLowerCase () " +
str.toLowerCase());
System.out.println(" toUpperCase() " +
str.toUpperCase ());
System.out.println { " trim() " +
str.trim());
System.out.println("\nString is still \"" + str + "\"\n")

Листинг 8.17. Выходные данные StringTest

Prompt> Java StringTest Geographic


String is "Geographic"
charAt(3) g
compareTo (Moscow) -6
concat(SuFFiX) GeographicSuFFiX
endsWith (hie) true
== Geographic false
equals(geographic) false
equalsIgnoreCase(geographic) true
indexOf('o') 2
indexOf Ci',5) 8
indexOf Co',5) -1
indexOf(rap) 4
indexOf(rap, 5) -1
lastlndexOf Co') 2
lastlndexOf Ci',5) -1
lastlndexOf Co',5) 2
lastlndexOf(rap) 4
lastlndexOf (rap, 5) 4
lengthO 10
replace Сc','k') Geographik
startsWith(eog, 1) true
startsWith (eog) false
substring (3) graphic
substring(3,8) graph
toLowerCase() geographic
toUpperCase0 GEOGRAPHIC
trimO Geographic

String is still "Geographic'


8.9. Массивы 291

Конструкторы
public StringO
Данный конструктор создает строку нулевой длины (но не объект n u l l ) .

public String(byte[ ] bytes)


public String(byte[ ] bytes, String encoding)
public String(byte[ ] bytes, int startlndex, int count)
public String(byte[ ] bytes, int startlndex, int count. String encoding)
Указанные конструкторы создают объект S t r i n g на базе байтового массива.

public String(char[ ] chars)


public String(char[ ] chars, int startlndex, int count)
Данные конструкторы создают строку на основе символьного массива.

public String(String string)


Данный конструктор копирует объект S t r i n g . Новый объект идентичен исход­
ному; в этом можно убедиться, вызвав метод e q u a l s . Оператор == вернет значе­
ние n u l l .

public String(StringBuffer stringBuffer)


Данный конструктор преобразует S t r i n g B u f f e r в S t r i n g .

8.9. Массивы
Массивы — это простые и эффективные структуры данных, имеющиеся практиче­
ски во всех языках программирования. Массивы обеспечивают доступ к наборам дан­
ных простых типов или объектов и позволяют обращаться к различным значениям
посредством одного идентификатора. Доступ к различным элементам массива осуще­
ствляется за одно и то же время. Массивы реализуются как объекты, обладающие сле­
дующими свойствами.
• Длина массива содержится в поле l e n g t h .
• Элементы массива могут соответствовать объектам Object либо переменным
конкретных типов.
• Подобно объектам, ссылки на массивы передаются при вызове методов.
Нумерация элементов массивов начинается с нуля, поэтому если в массиве содер­
жится 10 элементов, то их индексы лежат в диапазоне от О до 9. Как правило, массивы
создаются в два этапа: сначала выделяется память под элементы массива, а потом
этим элементам присваиваются значения. Если создается массив из простых типов
данных, каждый элемент массива инициализируется значением по умолчанию для
этого типа. При создании массива объектов его элементы инициализируются значе­
ниями n u l l . Присваивая элементам значения, следите за тем, чтобы индекс оставал­
ся в пределах массива.
292 Глава 8 . С и н т а к с и с Java

Н и ж е описываются два способа создания массива. П е р в ы й способ предполагает


создание массива в два этапа: сначала объявляется размер массива, а затем элементам
массива присваиваются значения. Второй способ отличается тем, что значения при­
сваиваются при объявлении массива.

Создание массива в два этапа


На первом этапе объявляется массив требуемого размера и типа.
i n t [ ] v a l u e s = new i n t [ 2 ] ; / / Двухэлементный массив
P o i n t [ ] p o i n t s = new P o i n t [ 5 ] ; / / Пятиэлементный массив
i n t numNames = askHowManyNames(); / / Длина м а с с и в а определяется
/ / при выполнении программы
S t r i n g [ ] names = new S t r i n g [ n u m N a m e s ] ;
Ha этом этапе объекты, которые должны принадлежать массиву, еще не создают­
ся. Присвоение значений осуществляется отдельно. Н а п р и м е р :
values[0] = 10;
values[1] = 100;
f o r ( i n t i=0; i < p o i n t s . l e n g t h ; i++) {
points[i] = new P o i n t ( i * 2 , i * 4 ) ;
}
f o r ( i n t j=0; j<names.length; j++) {
n a m e s [ j ] = "Name " + j ;
}

Часто разработчики упускают из виду второй этап создания массива. В этом случае
при обращении к элементу массива генерируется исключение N u l l P o i n t e r E x c e p t i o n .
Внимание!

Следующее выражение создает п ссылок на SomeObject; экземпля­


ры объектов SomeObject не создаются.
SomeObject [] objArray = new SomeObject [n] ;

Массив не обязательно должен быть инициализирован сразу после объявления;


между объявлением и инициализацией может п р о й т и определенное время. Если вы
ярый сторонник программирования на С, то для объявления массива можете приме­
нять выражение
Туре someVar[] = ...
вместо
Туре[] someVar = ...

Однако помните, что использование такого похода снижает ваш показатель J H P


Qava Hipness Factor) на 2,5 единицы.
8.9. Массивы 293

Создание массива в один этап


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

i n t [ ] v a l u e s = { 10, 100 };
P o i n t [ ] p o i n t s = { new P o i n t ( 0 , 0),
new P o i n t ( 2 , 4),
new P o i n t ( 4 , 8),
... };

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


поместить в массив, и соответственно объявляет длину массива. В листинге 8.18 пока­
зан п р и м е р создания массива в один этап и передачи массивов другим методам. Ре­
зультаты выполнения этой программы представлены в листинге 8.19.

Листинг 8 . 1 8 . G o l f . J a v a

/•к-к Р е з у л ь т а т ы и г р в г о л ь ф . */

public class Golf {


public static void main(String[] args) {
int[] pars = { 4,5,3,4^5,4,4,3,4 };
int [ ] scores = { 5,6,3,4,5,3,2,4,3 };
report(pars, scores);
}

/•• Отчет об одном раунде. */

public static void report(int[] pars, int[] scores) {


for(int i=0; i<scores.length; i++) {
int hole = i+1;
int difference = scores[i] - pars[i];
System.out.println("Hole " + hole + ": " +
diffToString(difference));
}
}

/** Преобразование представления. */

public static String diffToString(int diff) {


String[] names = {"Eagle", "Birdie", "Par", "Bogey",
"Double Bogey", "Triple Bogey", "Bad"};
// Если diff = -2, возвращается names[0], или "Eagle".
int offset = 2;
return(names[offset + diff]);
}
294 Глава 8 . С и н т а к с и с Java

Листинг 8 . 1 9 . Выходные данные G o l f

Proinpt> Java Golf


Hole 1: Bogey
Hole 2: Bogey-
Hole 3: Par
Hole 4: Par
Hole 5: Par
Hole 6: Birdie
Hole 7: Eagle
Hole 8: Bogey
Hole 9: Birdie

Многомерные массивы
в языке программирования Java многомерные массивы реализуются как "массивы
массивов", и в этом заключается очередное сходство между Java и C++. Следующий
фрагмент программы объявляет массив, состоящий из 12x14 элементов, и заполняет
его значениями.
i n t [ ] [ ] v a l u e s = new i n t [ 1 2 ] [ 1 4 ] ;
f o r ( i n t i = 0 ; i < 1 2 ; i++) {
f o r ( i n t j = 0 ; j < 1 4 ; j++) {
values[i][j] = someFunctionOf(i, j ) ;
}
}
Для обращения к конкретным элементам используются выражения наподобие
следующих:
i n t someVal = v a l u e s [ i ] [ j ] ; // i<12, j<14
v a l u e s [ i ] [ j ] = someint;
He указывая второй индекс, можно получить доступ сразу к целому массиву.

i n t [ ] someArray = v a l u e s [ i ] ; // 0<=i<=ll
v a l u e s [ i ] = someOtherArray;
Аналогичным образом создаются массивы с размерностью больше двух. Вложен­
ные массивы не обязательно должны иметь одинаковую длину. Чтобы реализовать
"непрямоугольные" массивы, надо не указывать второй размер. Ниже приведен при­
мер такого массива.
S t r i n g [ ] [ ] names = new S t r i n g [ 3 ] [ ] ;
S t r i n g [ ] nameO = { " J o h n " , " Q . " , " P u b l i c " };
S t r i n g [ ] namel = { " J a n e " , "Doe" };
S t r i n g [ ] name2 = { " P e l e " };
n a m e s [ 0 ] = nameO; / / 3 э л е м е н т а
names[1] = namel; / / 2 элемента
n a m e s [ 2 ] = name2; / / 1 элемент
8.10. Векторы 295

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


String[][] altNames = { { "John", "Q.", "Public" },
{ " J a n e " , "Doe" },
{ "Pele" }
};

8.10. Векторы
Массивы — чрезвычайно полезное средство Java, однако они имеют один недоста­
ток: в процессе выполнения программы нельзя увеличить размеры массива. Для того
чтобы преодолеть это ограничение, в Java реализованы "расширяемые" массивы, или
векторы, которые реализуются посредством класса j a v a . u t i l . V e c t o r . Класс V e c t o r
отличается тем, что позволяет добавлять или удалять элементы в любой позиции.
Обычно он используется для тех же целей, что и связные списки. Класс V e c t o r обес­
печивает доступ к конкретному элементу за фиксированное время, однако интервал
времени, который требуется для включения элемента в начало или в середину
V e c t o r , пропорционален общему числу элементов.
Н и ж е описаны основные методы класса V e c t o r ; пример их использования будет
приведен в следующем разделе. О б р а т и т е внимание, что методы для извлечения зна­
чений возвращают тип O b j e c t .

Конструкторы
p u b l i c Vector()
p u b l i c Vector(int initialCapacity)
p u b l i c Vector(int initialCapacity, int c a p a c i t y l n c r e m e n t )
Эти конструкторы создают пустой объект V e c t o r . Если при вызове конструктора
емкость вектора (число элементов, содержащихся в нем) не указана, она устанав­
ливается равной 10 элементам, однако при необходимости Java автоматически ко­
пирует данные в массив большего размера.

Методы
p u b l i c void a d d E l e m e n t ( O b j e c t object)
p u b l i c v o i d i n s e r t E l e m e n t A t ( O b j e c t object, int i n d e x )
p u b l i c v o i d s e t E l e m e n t A t ( O b j e c t object, int i n d e x )
Д а н н ы е с и н х р о н и з и р о в а н н ы е методы добавляют элементы к объекту V e c t o r . Ме­
тод a d d E l e m e n t присоединяет объект к концу массива, а остальные два метода
включают его в указанной позиции. П р и выполнении метода i n s e r t E l e m e n t A t
объекты с индексами, равными и большими, чем указанный, сдвигаются вправо на
один элемент. Метод s e t E l e m e n t A t замещает элемент в указанной позиции.

p u b l i c int capacityO
Метод c a p a c i t y возвращает текущий размер массива, в котором хранятся эле­
менты объекта V e c t o r . П р и добавлении новых элементов размер массива может
быть увеличен.
296 Глава 8. Синтаксис Java

public boolean contains(Object object)


Метод c o n t a i n s определяет, содержится ли в составе V e c t o r объект, равный
указанному (для проверки равенства используется метод e q u a l s ) .

public void copyInto(Object[ ] newArray)


Данный синхронизированный метод копирует ссылки на объекты в указанный
массив.

public Object elementAt(int index)


Синхронизированный метод element At возвращает элемент в указанной позиции.

public Enumeration elements()


Класс Java . u t i l . Enumeration предоставляет интерфейсные средства, исполь­
зуемые при работе с наборами элементов. Метод e l e m e n t s возвращает объект
Enumeration, соответствующий объекту Vector.

public void ensureCapacity(int minimum)


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

public Object firstElement()


public Object lastElementO
Указанные синхронизированные методы возвращают соответственно первый и
последний элементы объекта Vector.

public int indexOf(Object object)


public int indexOf(Object object, int startlndex)
public int lastlndexOf(Object object)
public int lastlndexOf (Object object, int startlndex)
Данные синхронизированные методы возвращают индекс соответственно перво­
го и последнего элементов, равного указанному объекту (для проверки равенства
используется метод e q u a l s ) .

public boolean isEmpty()


Если объект V e c t o r содержит элементы, метод i s Empty возвращает значение
f a l s e ; в противном случае возвращается t r u e .

public boolean removeElement(Object object)


public void removeElementAt(int index)
public void removeAllElements()
Данные синхронизированные методы позволяют удалять элементы из объекта
Vector.
8 . 1 1 . Пример создания простого бинарного дерева 297

p u b l i c void setSize(int n e w S i z e )
Синхронизированный метод s e t S i z e задает размер объекта V e c t o r . От метода
e n s u r e C a p a c i t y он отличается тем, что если в объекте V e c t o r содержится
большее число элементов, чем указано, он усекается.

p u b l i c int size()
Метод s i z e возвращает число элементов в объекте V e c t o r (текущий размер мас­
сива может превышать число элементов).

p u b l i c void trimToSize()
Данный с и н х р о н и з и р о в а н н ы й метод устанавливает размеры массива, в котором
хранятся элементы объекта V e c t o r , так, что в нем может находиться число эле­
ментов, в точности равное текущему. Если в процессе работы элементы могут до­
бавляться или удаляться, использовать этот метод не следует, однако, он поможет
сэкономить память, если число элементов в объекте V e c t o r фиксировано.
Многие методы класса V e c t o r синхронизированы для того, чтобы предотвратить
проблемы, возникающие п р и попытке обращения к одним и тем же данным из раз­
ных потоков. Синхронизация снижает производительность программы, поэтому в
Java 2 предусмотрены два н е с и н х р о н и з и р о в а н н ы х класса A r r a y L i s t и L i n k e d L i s t ,
которые действуют аналогично классу V e c t o r . Однако эти классы доступны только в
JDK 1.2 и новых версиях данного пакета и не поддерживаются большинством броузе­
ров, в среде которых выполняются аплеты. Дополнительная и н ф о р м а ц и я о синхро­
низации и поддержке многопотоковых программ приведена в главе 16.

8 . 1 1 . Пример создания простого бинарного


дерева
В данном разделе показано, как ссылки и класс V e c t o r могут быть использованы для
создания класса бинарного дерева. Эта структура включает метод d e p t h F i r s t S e a r c h ,
предназначенный для поиска в глубину (при таком поиске выбирается левый узел и
предпринимается попытка достигнуть наибольшей глубины). Данный метод является
рекурсивным; применение рекурсии— самый естественный подход при организации
поиска по дереву. Метод d e p t h F i r s t S e a r c h также использует интерфейс
N o d e O p e r a t o r (листинг 8.21) как обобщенный оператор, который может быть выпол­
нен на любом узле. Этот и н т е р ф е й с позволяет вам изменять действия, выполняемые
над деревом, не модифицируя при этом класс Node (листинг 8.22). В состав данной
структуры также входит метод b r e a d t h F i r s t S e a r c h , который использует V e c t o r для
построения очереди при обходе дерева "в ширину" (перед тем как перейти к следующе­
му уровню, просматриваются все узлы, принадлежащие текущему уровню).
В Java 2 в состав базовых языковых средств помимо деревьев были включены и
другие структуры данных. Дополнительную информацию по этому вопросу вы найде­
те по адресу h t t p : / / j a v a . s u n . e o m / j 2 s e / l . 3 / d o c s / g u i d e / c o l l e c t i o n s / . Java
Collections Framework предоставляет расширенный API для п о с т р о е н и я и обработки
различных структ)р. В частности, API обеспечивает поддержку хеш-таблиц, расши-
298 Глава 8. Синтаксис Java

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


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

Листинг 8.20. Node. Java

import Java.util.Vector;

/•• Структура данных, представляющая узел бинарного дерева.


* Она содержит значение узла и ссылки (указатели) на
* левое и правое поддеревья.
Ч
public class Node {
private Object nodeValue;
private Node leftChild, rightChild;
/** Создание Node с указанным значением и ссылками на
* поддеревья.

public Node(Object nodeValue, Node leftChild,


Node rightChild) {
this.nodeValue = nodeValue;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
/** Построение Node с указанным значением и левым
* поддеревом. Вместо ссылки на правое поддерево
* используется значение null. Если ссылка на оба
* поддерева равна null, следует использовать
* конструктор Leaf.
V
public Node(Object nodeValue, Node leftChild) {
this(nodeValue, leftChild, null);
}
/** Получение значения узла. */

public Object getNodeValue() {


return(nodeValue) ;
}
/•• Установка значения узла. */
public void setNodeValue(Object nodeValue) {
this.nodeValue = nodeValue;
}
/** Получение левого поддерева. */
public Node getLeftChild() {
8.11. Пример создания простого бинарного дерева 299

return(leftChild);

/*• Установка левого поддерева. */


public void setLeftChild(Node leftChild) {
this.leftChild = leftChild;
}
Z*'^ Получение правого поддерева. */
public Node getRightChild() {
return(rightChild);
}
/•• Установка правого поддерева. */
public void setRightChild(Node rightChild) {
this.rightChild = rightChild;
}
/** Обход дерева "в глубину" с применением к каждому узлу
* указанной операции.
V
public void depthFirstSearch(NodeOperator op) {
op.operateOn(this);
if (leftChild != null) {
leftChild.depthFirstSearch(op);
}
if (rightChild != null) {
rightChild.depthFirstSearch(op);
}
}
/** Обход дерева "в ширину" с применением к каждому узлу
* указанной операции.
V
public void breadthFirstSearch(NodeOperator op) {
Vector nodeQueue = new Vector();
nodeQueue.addElement(this);
Node node;
while('nodeQueue.isEmpty0) {
node = (Node)nodeQueue.elementAt(0);
nodeQueue.removeElementAt(0);
op.operateOn(node);
if (node.getLeftChildO != null) {
nodeQueue.addElement(node.getLeftChild());
}
if (node.getRightChild0 != null) {
nodeQueue.addElement(node.getRightChild());
}
}
}
300 Глава 8. Синтаксис Java

Листинг 8.21. NodeOperator.Java

/•• Интерфейс, который класс Node использует для того, чтобы


* убедиться, что объект содержит метод operateOn.
V
public interface NodeOperator {
void operateOn(Node node);

Листинг 8.22. Leaf.Java

/** Лист: узел без поддеревьев. */

public class Leaf extends Node {


public Leaf(Object value) {
super(value, null, null);
}

Теперь, когда общая стр)^тст)ра данных готова, напишем тестовый пример. На рис. 8.1
показано простое дерево, а в листинге 8.23 представлен код класса, реализующего
N o d e O p e r a t o r и использующего классы Node и Leaf. Данный класс не выполняет ника­
кой обработки, а лишь выводит значения каждого из просмотренных узлов. Результаты
работы этого класса показаны в листинге 8.24.

Предложение

Существительное с прилагательным Глагольная конструкция

Прилагательное Существительное Переходный глагол Существительное

Java hackers hack Java

Рис. 8.1. Разбор предложения "Java hackers hack Java"

Листинг 8.23.TreeTest.Java

/** Класс NodeOperator, который выводит значение каждого узла. */

class PrintOperator implements NodeOperator {


public void operateOn(Node node) {
System.out.println(node.getNodeValue());
}
}
Пример дерева, демонстрирующего разбор предложения
8 . 1 1 . Пример создания простого бинарного дерева 301

* "Java hackers hack Java" с использованием простой


* контекстно-свободной грамматики.
V
public class TreeTest {
public static void main(String[] args) {
Node adjective =
new Node(" Adjective", new Leaf(" Java"));
Node nounl =
new Node(" Noun", new Leaf(" hackers"));
Node verb =
new Node(" TransitiveVerb", new Leaf(" hack")),
Node noun2 =
new Node{" Noun", new Leaf(" Java"));
Node np = new Node(" NounPhrase", adjective, nounl),
Node vp = new Node(" VerbPhrase", verb, noun2);
Node sentence = new Node("Sentence", np, vp);
PrintOperator printOp = new PrintOperator();
System.out.println("Depth first traversal:");
sentence.depthFirstSearch(printOp);
System.out.println("\nBreadth first traversal:");
sentence.breadthFirstSearch(printOp);

Листинг 8.24. Выходные данные TreeTest

Prompt> Java TreeTest


Depth first traversal:
Sentence
NounPhrase
Adjective
Java
Noun
hackers
VerbPhrase
TransitiveVerb
hack
Noun
Java
Breadth first traversal:
Sentence
NounPhrase
VerbPhrase
Adjective
Noun
TransitiveVerb
Noun
Java
hackers
hack
Java
302 Глава 8. Синтаксис Java

8.12. Исключения
В Java реализовано мощное средство обработки ошибок — исключения. Исключе­
ние может быть сгенерировано в одном блоке кода, а обработано в другом блоке или
даже в другом методе (из которого вызывается метод, генерирующий исключение).
Исключения в Java отличаются от исключений в C++. Во-первых, в Java в конструкции
t r y / c a t c h предусмотрен блок f i n a l l y ; в него помещается фрагмент кода, который
должен быть выполнен независимо от того, было ли сгенерировано исключение.
Второе отличие заключается в том, что, создавая метод, можно потребовать от раз­
работчика, применяющего его, включить в программу обработку исключения, кото­
рое может генерировать этот метод. Если разработчик не выполнит данное требова­
ние, код программы не будет скомпилирован.

Обработка исключения
Простейшая конструкция, предназначенная для обработки исключения, имеет
следующий вид:
try {
// Выражение 1
// Выражение 2

} catch(SomeException someVar) {
handleTheException(someVar);
}
Например, конструктор j a v a . n e t . URL может генерировать исключение J a v a ,
n e t . Malf o r m e d U R L E x c e p t i o n , a метод r e a d L i n e класса j a v a . i o . Buf f e r e d R e a d e r —
исключение J a v a . i o . l O E x c e p t i o n . Код, представленный в листинге 8.25, использует
как конструтстор URL, так и метод r e a d L i n e , поэтому в нем предусмотрена обработка
обоих возможных исключений. Как видно из листинга 8.26, исключение
Malf o r m e d U R L E x c e p t i o n генерируется в результате проверки формата URL. Попытка
скопировать ресурс и даже проверить, существует ли он, не предпринимается. Приме­
ры обращения к файлам по сети будут приведены в главе 17, посвященной сетевому
программированию.

Листинг 8.25. URLTest.java

import j a v a . n e t . * ; / / Для URL M a l f o r m e d U R L E x c e p t i o n


import j a v a . i o . * ; / / Для B u f f e r e d R e a d e r

/ * * К л а с с / демонстрирующий и с п о л ь з о в а н и е блоков try/catch.

p u b l i c c l a s s URLTest {
public s t a t i c void main(String[] args) {
URLTest t e s t = new URLTest Ob­
t e s t . getURL () ;
test.printURLO ;
}

p r i v a t e URL u r l = n u l l ;
8.12. Исключения 303

/** Получение строки и создание на ее основе объекта URL.


* Если при чтении строки возникла ошибка, выводится
* сообщение. Если чтение строки окончилось успешно, но
* создать URL не удалось, выводится сообщение об ошибке
* и предпринимается еще одна попытка.
Ч
public URL getURLO {
if (url 1= null) {
return(url);
}
System.out .print ("Enter URL: 'Mr-
System, out.flush();
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
String urlString;
try {
urlString = in.readLine();
} catch(lOException ioe) {
System.out.println("lOError when reading input: " + ioe);
ioe.printStackTrace(); // Отобразить стек вызовов.
return(null);
}
try {
url = new URL(urlString);
} catch(MalformedURLException mue) {
System.out.println(urlString + " is not valid.\n" +
"Try again.");
getURL();
}
return(url);
}
/** Вывод информации об URL. */

public void printURLO {


if (url == null) {
System, out.println("No URL.");
} else {
String protocol = url.getProtocol();
String host = url.getHost ();
int port = url.getPort();
if (protocol.equals("http") && (port == -1)) {
port = 80;
}
String file = url.getFile();
System.out.println("Protocol: " + protocol +
"\nHost: " + host +
"\nPort: " + port +
"\nFile: " + file);
}
}
304 Глава 8 . С и н т а к с и с Java

Листинг 8 . 2 6 . Результаты выполнения URLTest

> Java URLTest


> Enter URL: http://java.sun.com/ConvertingToActiveX.htinl
Protocol: http
Host: java.sun.com
Port: 80
File: /ConvertingToActiveX.html

Обратите внимание на вызов p r i n t S t a c k T r a c e в теле метода getURL. В резуль­


тате этого обращения отображается стек вызова методов, соответствующий той точ­
ке выполнения программы, в которой было сгенерировано исключение. В некоторых
реализациях при этом также выводятся номера строк исходных файлов. Использова­
ние данного метода удобно при отладке программ; его иногда применяют даже в тех
случаях, когда исключения не генерируются. Например, следующий вызов отобража­
ет содержимое стека:
new ThrowableO . p r i n t S t a c k T r a c e () ;

Использование нескольких блоков catch


Одному блоку t r y могут сопутствовать несколько блоков c a t c h . При возникнове­
нии исключения Java выполняет первый блок c a t c h , параметр которого соответству­
ет типу сгенерированного исключения. Поскольку исключения, как и классы Java,
имеют иерархическую структуру, можно расположить блоки c a t c h так, чтобы обра­
ботчики конкретных исключений предшествовали обработке более общих случаев.
Обратите внимание на код, приведенный в листинге 8.27. По сравнению с примером,
рассмотренным выше, метод getURL несколько упрощен за счет использования одно­
го блока t r y с двумя блоками c a t c h . Заметьте также, что блок c a t c h , обрабатываю­
щие! исключение MalformedURLException, должен быть расположен перед обра­
ботчиком lOException, поскольку MalformedURLException является подклассом
lOException.
М е т о д и к а профессионалов

При использовании нескольких блоков catch более конкретные об­


работчики должны предшествовать более общим.

Листинг 8.27. Модифицированный метод getURL

p u b l i c URL getURLО {
i f ( u r l != n u l l ) {
return(url);
}
System.out.print("Enter URL: " ) ;
System.out.flush() ;
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
8.12. Исключения 305

string urlString = null;


try {
urlString = in.readLine{);
url = new URL(urlString);
} catch(MalformedURLException mue) {
System.out.println(urlString + " is not valid.\n" +
"Try again.");
getURL();
} catch(lOException ice) {
System.out.println("lOError when reading input: " + ioe)
ice.printStackTrace(); // Show stack dump
return(null);
}
return(url);

Выражение finally
После блоков c a t c h можно указать блок f i n a l l y , который будет выполняться в
любом случае, независимо от того, было ли сгенерировано исключение. Он получает
управление даже тогда, когда в блоке t r y или c a t c h присутствуют выражения b r e a k ,
c o n t i n u e или r e t u r n . В листинге 8.28 показан третий вариант метода getURL, ис­
пользующий блок f i n a l l y .

Листинг 8.28. Метод getURb с использованием блока f i n a l l y

p u b l i c URL getURLО {
i f ( u r l != n u l l ) {
return(url);
}
System.out.print("Enter URL: " ) ;
System.out.flush();
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
String urlString = null;
try {
urlString = in.readLine();
url = new URL(urlString);
} catch(MalformedURLException mue) {
System.out.println(urlString + " is not valid.\n" +
"Try again.");
getURL();
} catch(lOException ioe) {
System.out.println("lOError when reading input: " + ioe)
ioe.printStackTrace0; // Можно пропустить return(null)
} finally {
return(url);
}
}
306 Глава 8. Синтаксис Java

Использование ключевого слова throws и явная


генерация исключений
Создавая метод, в котором может генерироваться одно или несколько исключе­
ний, не обязательно включать их обработку в тело метода. Достаточно объявить, что
при выполнении метода могут возникать исключения. Это объявление имеет сле­
дующий формат:
p u b l i c SomeType s o m e M e t h o d ( . . . ) throws SomeException {
или
public SomeType someMethod(...)
throws ExceptionTypel, ExceptionType2 {
П р и таком подходе у вас появляется возможность предъявить разработчику, при­
меняющему ваш метод, требование включить в его программу обработчики конкрет­
ных исключений. Кроме того, объявление исключений, генерируемых при выполне­
нии метода, позволяет отказаться от п р и м е н е н и я обработчиков в теле метода и пере­
нести их в вызывающий метод.
В код программы можно включить выражение t h r o w , которое явным образом ге­
нерирует исключение. П р и м е р ы таких выражений приведены ниже.
t h r o w new l O E x c e p t i o n ( " B l o c k e d by f i r e w a l l " ) ;
t h r o v ; new MalformedURLException (
"Invalid protocol: telephone");
Как правило, t h r o w используется при работе с исключениями, определяемыми
разработчиком. Новое исключение определяется как подкласс уже существующего
исключения. В листинге 8.29 приведен п р и м е р исключения, которое можно приме­
нять при построении геометрических объектов, требующих неотрицательных значе­
ний параметров (высоты, ш и р и н ы , радиуса и т.д.) Результаты использования данного
класса показаны в листинге 8.30.

!
I Листинг 8 . 2 9 . N e g a t i v e L e n g t h E x c e p t i o n . Java
import java.io.*;

public class NegativeLengthException extends Exception {

/** Проверка исключения NegativeLengthException */

public static void main(String[] args) {


try {
int lineLength = readLength();
for(int i=0; i<lineLength; i++) {
System.out.print("*") ;
}
System.out.println() ;
} catch (NegativeLengthException nle) {
System.out.println("NegativeLengthException: " +
nle.getMessage());
}
8.12. Исключения 307

public NegativeLengthException() {
super("Negative dimensions not permitted.");
}
public NegativeLengthException(String message) {
super(message) ;

// При выполнении метода readLength может быть сгенерировано


// исключение lOExceptions. Оно обрабатывается локально, но
// обработка исключения NegativeLengthException осуществляется
// в теле вызывающего метода.
private static int readLength() throws NegativeLengthException {
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("Enter length: ") ;
System.out.flush() ;
int len = 0;
try {
String line = in.readLine();
len = Integer.parseint (line);
if (len < 0) {
throw new NegativeLengthException0;
}
} catch (lOException ioe) {
System.out.println("Problem reading from keyboard");
}
return(len);
}

Листинг 8.30. Генерация N e g a t i v e L e n g t h E x c e p t i o n

> Java NegativeLengthException


> Enter length: 4

> Java NegativeLengthException


> Enter length: -345
NegativeLengthException: Negative dimensions not permitted.

Необрабатываемые исключения
Исключения, о которых шла речь выше считаются обрабатываемыми. При созда­
нии программы вы обязаны предусмотреть их обработку. Java также включает два
класса необрабатываемых исключений: E r r o r и R u n t i m e E x c e p t i o n . Вы имеете пра­
во включить в создаваемый класс соответствующие обработчики, но не обязаны это
делать, поскольку данные исключения могут возникать в самых различных точках
308 Глава 8. Синтаксис Java

программы. В качестве примеров подклассов E r r o r можно привести OutOfMemory-


E r r o r и C l a s s F o r m a t E r r o r . Исключение O u t O f M e m o r y E r r o r возникает тогда, ко­
гда при объявлении массива или выполнении оператора new обнаруживается нехват­
ка памяти. C l a s s F o r m a t E r r o r генерируется в том случае, когда формат файла класса
некорректен (это может произойти, если файл класса копировался средствами FTP
не в двоичном, а в символьном режиме).
К числу подклассов класса R u n t i m e E x c e p t i o n относятся, в частности,
A r i t h m e t i c E x c e p t i o n (деление на нуль) и A r r a y l n d e x O u t O f B o u n d s E x c e p t i o n
(обращение за пределы массива). В пользовательских программах R u n t i m e E x c e p -
t i o n часто перехватывается для обработки таких исключений, как I l l e g a l A r g u -
mentException.

8.13. Резюме
В данной главе рассказывалось о синтаксисе Java-программ. Здесь были вкратце рас­
смотрены простые и ссылочные типы, операторы, класс Math, строки, массивы, векто­
ры и исключения. Освоив этот материал и имея общее представление об объектно-
ориентированном программировании (глава 7), вы можете приступать к изучению кон­
кретных вопросов программирования на Java, рассмотренных в следующих главах.
АПЛЕТЫ
И ОСНОВНЫЕ
ДЕЙСТВИЯ
С ГРАФИКОЙ

В ЭТОЙ главе...

• Создание аплетов: Java-программы, встраиваемые


в Web-страницы.
• Жизненный цикл аплета.

• Передача параметров аплетам.

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

• Загрузка и рисование изображений.

• Предварительная загрузка изображений.

• Управление загрузкой изображений с помощью


MediaTracken
J~y\:EJ^zJ

этой главе мы обсудим два основных типа графических Java-программ: аплеты и

В приложения. Мы покажем вам, как создаются аплеты, связываются с Web-


страницами и как им передаются параметры с помощью элемента PARAM. Гово­
ря о приложениях, мы рассмотрим один из подходов к созданию окон. Альтернатив­
ные способы будут обсуждаться в главе 13. Основные операции рисования примени­
мы как для аплетов, так и для приложений, однако в приложениях чаще используются
графические средства Java2D, о которых речь пойдет в главе 10. Здесь мы также уде­
лим внимание способам загрузки и вывода изображений в аплетах и в приложениях.

9 . 1 . Что такое аплеты


Аплет— это специальный тип Java-программы, предназначенный для встраивания
в состав Web-страницы. В отличие от программ, которые выполняются удаленно на
HTTP-сервере, аплет, содержащийся в составе Web-страницы, выполняется локально,
т.е. на клиентской машине в среде броузера. В связи с этим особую важность приоб­
ретают вопросы защиты. Операции, которые вполне допустимы в Java-программах
общего назначения, или приложениях, в Java-аплетах должны быть запрещены. Так,
например, если Java-приложение может удалять файлы на диске, то для Java-аплетов
эта операция недопустима. Необходимые ограничения реализуются в клиентской
системе с помощью объекта S e c u r i t y M a n a g e r . В версии 1.1 и более поздних реали­
зациях платформы Java поддерживается цифровая подпись классов. Пользователь
может указать диспетчеру защиты на то, что некоторые обычно запрещенные дейст­
вия должны быть разрешены для классов, подписанных определенными организа­
циями или разработчиками. Конкретный набор ограничений, действующих при ра­
боте аплета, зависит от реализации SecurityManager. Диспетчер защиты, исполь­
зуемый по умолчанию в Netscape и Internet Explorer, запрещает аплетам выполнять
следующие операции.
312 Глава 9. Аплеты и основные действия с графикой

Ч т е н и е данных с локального диска


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

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

Вызов локальных программ


С помощью метода e x e c класса R u n t i m e Java-приложения могут запускать про­
граммы, установленные на локальном компьютере, а с помощью платформенно-
зависимых (native) методов— вызывать модули, написанные на С или C++. В апле-
тах эти действия запрещены, поскольку они могут создавать угрозу безопасности
системы.

Извлечение информации о пользователе


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

9.2. Создание аплета


Создание аплета предусматривает два этапа: построение Java-класса и формирова­
ние HTML-документа, включающего аплет. Java-класс определяет поведение аплета, а
HTML-документ выделяет аплету прямоугольную область в составе Web-страницы.

Шаблон аплета
Т и п и ч н а я структура аплета представлена в листинге 9.L Как правило, аплет со­
держит определения переменных экземпляра, методы i n i t и p a i n t . Метод i n i t ав­
томатически вызывается броузером в процессе создания аплета. Затем, когда аплет
готов к выводу информации, вызывается метод p a i n t . Метод p a i n t также автомати­
чески вызывается в тех случаях, когда изображение на некоторое время оказалось пе­
р е к р ы т ы м другим окном и должно быть восстановлено, а также при добавлении ком­
понентов. Этот метод может быть вызван и программным путем. Реализации методов
i n i t и p a i n t , используемые по умолчанию, не выполняют никаких действий; пред­
полагается, что при создании аплета эти методы будут переопределены. Помимо
i n i t и p a i n t , существуют и другие предопределенные методы (они будут рассмотре­
ны далее в этой главе), однако, чтобы создать простой аплет, достаточно переопре-
9.2. Создание an лета 313

делить два указанных метода. Как вы знаете, в Java, в отличие от таких языков, как С и
C++, разрешается инициализировать переменные экземпляра при их определении в
теле класса, но поступать таким образом при создании аплета не рекомендуется. Дело
в том, что н е к о т о р ы е т и п ы графических объектов запрещается инициализировать до
вызова метода i n i t . Поэтому, вместо того чтобы запоминать, какие из объектов
можно инициализировать непосредственно, лучше п р и н я т ь за правило выполнять
всю инициализацию в теле метода i n i t . В отличие от переменных экземпляра, дос­
тупных всем методам класса, переменные, которые нужны лишь на время выполне­
ния i n i t , следует объявлять как локальные.

Листинг 9.1. Шаблон Java-аплета

import Java.applet.Applet;
import j a v a . a w t . * ;

p u b l i c c l a s s AppletTemplate extends Applet {

/ / Объявления переменных.

public void i n i t ( ) {
/ / Инициализация переменных, з а г р у з к а изображений и т . д .
}

p u b l i c v o i d p a i n t ( G r a p h i c s g) {
/ / Операции р и с о в а н и я .

Шаблон HTML'фaйлa
После создания и компиляции аплета полученный файл класса должен быть связан с
Web-страницей. Для этого используется дескриптор APPLET, показанный в листинге 9.2.
Подробнее данный дескриптор будет описан в разделе 9.6, сейчас же заметьте, что
обязательный атрибут CODE задает имя файла класса Java, кроме него в составе деск­
риптора также должны присутствовать атрибуты WIDTH и HEIGHT. Если аплет поме­
щен не в тот каталог, в котором находится связанный с ним HTML-документ, для ука­
зания расположения аплета должен использоваться атрибут CODEBASE. После того
как аплет скомпилирован и связан с Web-страницей, загрузка этой страницы в броузер
автоматически приведет к запуску аплета. Разработчики часто присваивают одинако­
вые имена аплету и связанному с ним Web-документу (например, HTML-файл
A p p l e t T e m p l a t e . h t m l и аплет A p p l e t T e m p l a t e . c l a s s ) , но на самом деле имена
аплета и Web-страницы никак не связаны. Более того, в одном HTML-документе мо­
жет содержаться несколько аплетов.
Скомпилированный файл класса аплета ( f i l e , c l a s s ) должен быть доступен че­
рез Web; на исходный код это требование не распространяется. Если исходные фай­
лы и файлы классов расположены в разных каталогах, помните, что все несистемные
файлы классов, используемые аплетом, должны быть доступны из Web и находиться
314 Глава 9 . Аплеты и основные д е й с т в и я с графикой

либо в том же каталоге, либо в каталогах, соответствующих иерархии пакетов. При


передаче файлов классов с одного компьютера на другой с помощью FTP необходимо
использовать двоичный режим; при копировании в текстовом режиме файлы классов
будут искажены.
В некоторых системах применяется переменная окружения CLASS PATH, которая
указывает, в каких каталогах следует искать файлы классов Java. Заметьте, что данная
переменная применима только к локальным программам, удаленный пользователь,
который копирует ваш аплет на свой компьютер, ничего не знает об установках
CLASS PATH на вашей машине. Более того, Netscape и Internet Explorer предоставляют
дополнительные привилегии файлам классов, на которые указывает переменная ок­
ружения CLASS PATH, поэтому необходимо убедиться, что CLASS PATH недоступна
процессу броузера. В противном случае злоумышленники, которые знают особенно­
сти CLASS PATH на вашем компьютере, могут использовать эти сведения для органи­
зации атаки.
Безопасность

При запуске броузера убедитесь в том, что переменная CLASSPATH


не установлена.

Л и с т и н г 9 . 2 . HTML-шаблон

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>A Template for Loading Applets</TITLE>
</HEAD>
<BODY>
<H1>A Template for Loading Applets</Hl>
<P>
<APPLET CODE="AppletTemplate.class" WIDTH=120 HEIGHT=60>
<B>Error! You must use a Java-enabled browser.</B>
</APPLET>

</BODY>
</HTML>

9.3. Пример аплета


в листинге 9.3 показан код аплета, соответствующий шаблону, который был при­
веден в предыдущем разделе. Подробно структура аплета будет рассмотрена далее в
этой главе, здесь же мы ограничимся лишь обсуждением основных элементов. В под­
классе класса Applet объявлена переменная экземпляра типа Image. В теле метода
i n i t по умолчанию задаются цвет и шрифт, загружается по сети файл изображения, а
содержимое этого файла связывается с объявленной ранее переменной Image. Кроме
9.3. Пример аплета 315

того, в этом методе аплету присваивается метка и выводится строка текста. Метод
paint выводит изображение, располагая его на 50 пикселей ниже верхнего края об­
ласти, выделенной для аплета. В листинге 9.4 представлен HTML-код документа, со­
держащего аплет, а результаты выполнения аплета в среде броузера Internet Explorer
5.0 (в системе Windows 2000) показаны на рис. 9.1.

Листинг 9,3. JavaJump.Java

import Java.applet.Applet;
import java.awt.*;

/** Аплет, который выводит изображение. */

public class JavaJump extends Applet {


private Image jumpingJava; // Объявление переменной экземпляра

public void init() { // Инициализация


setBackground(Color.white);
setFont(new Font("SansSerif", Font.BOLD, 18));
jumpingJava = getlmage(getDocumentBase(),
"images/Jumping-Java.gif");
add(new Label("Great Jumping Java!"));
System.out.println("Yow! I'm jiving with Java.");

public void paint(Graphics g) { // Рисование


g.drawlmage(jumpingJava, 0, 50, this);

Листинг 9.4, JavaJiimp. html

<IDOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Jumping Java</TITLE>
</HEAD>
<BODY BGCOLOR="BLACK" TEXT="WHITE">
<Hl>Jumping Java</Hl>
<P>
<APPLET CODE="JavaJump.class" WIDTH=250 HEIGHT=335>
<B>Sorry, this example requires Java.</B>
</APPLET>
</BODY>
</HTML>
316 Глава 9. Аплеты и основные действия с графикой

File Б(Й Vi0w Favorites Г-

Рис. 9.1. Результаты выполнения аплета,


^Оопе ^ ^ My Computer
представленного в листингах 9.3 и 9.4

Автоматическое обновление информации


в окне аплета
в большинстве случаев основные действия по выводу аплетом информации осущест­
вляются в теле метода p a i n t . П р и внесении изменений, требующих обновления изо­
бражения на экране, надо вызвать метод r e p a i n t , который, в свою очередь, вызывает
метод u p d a t e . В результате область, занимаемая аплетом, очищается и вызывается ме­
тод p a i n t . Подробно работу метода r e p a i n t и u p d a t e мы рассмотрим ниже, сейчас же
достаточно запомнить, что эти методы вызываются тогда, когда разработчик считает
нужным обновить содержимое экрана. Содержимого экрана может также обновляться
по инициативе системы. Обычно это происходит тогда, когда часть окна перекрывается
другим окном; после того, как окно снова станет активным, информацию в нем следует
отобразить повторно. В большинстве случаев разработчику не приходится заботиться о
вызове метода p a i n t . Если этот метод вызывается по инициативе системы, система ус­
танавливает область отсечения с помощью объекта G r a p h i c s (область отсечения соот­
ветствует части окна, которая исчезла из области видимости). В некоторых случаях, ко­
гда при разных вызовах метода p a i n t отображается различная информация, содержи­
мое окна может выводиться некорректно. Чтобы это не произошло, надо вызывать
метод r e p a i n t либо задавать самостоятельно область отсечения. Подробно данные во­
просы будут обсуждаться далее в этой главе.

Повторная загрузка аплета в процессе разработки


В Netscape и Internet Explorer аплеты автоматически сохраняются в кэше. В ре­
зультате после модификации кода аплета внесенные изменения не будут видны в окне
броузера, поскольку броузер использует предыдущую версию аплета, хранящуюся в
9.3. Пример аплета 317

кэше. Н о в ы й аплет можно загрузить принудительно. В Netscape для этого использует­


ся сочетание <Shift>+Reload (щелчок на кнопке Reload при нажатой клавише <Shift>),
а в Internet Explorer — сочетание <Control>+Reload.

Отображение данных, выведенных в стандартный


выходной поток
Для отображения символьной и н ф о р м а ц и и в рабочем аплете обычно создают тек­
стовую область либо используют другие управляющие элементы. (Подробно этот под­
ход будет обсуждаться в главе 13.) Одну строку текста можно также вывести в строке
состояния, используя для этого метод s h o w s t a t u s . Однако, в процессе отладки часто
возникает необходимость отображать несколько строк символов с помощью метода
S y s t e m , o u t . p r i n t l n . Возникает вопрос: где будут отображаться результаты вызова
этого метода? Это зависит от используемого броузера.

Стандартный вывод
в Netscape Navigator и н ф о р м а ц и я , которая выводится в стандартный выходной
поток, представляется в специальном окне. Для того чтобы отобразить это окно на
экране, надо выбрать пункт Java Console в меню Window. В Netscape C o m m u n i c a t o r
пункт J a v a Console находится в меню Tools, которое, в свою очередь, вызывается по­
средством меню Communicator. На рис. 9.2 показано, как выглядит окно Java Console
после выполнения аплета J a v a Jump. Ч т о б ы отобразить в окне список допустимых
команд, надо ввести символ "?".

Netscape Communications Corporation -- Java 1 1.5


Type 7'for options.
Symantec Java! ByteCode Compiler Version 210.065
Copyright (C) 1996-97 Symantec Corporation
Yow! I'm jiving with Java.

Рис. 9.2. В Netscape информация, выводимая


^ в стандартный выходной поток, отображается
в специальном окне Java Console

П р и работе в Internet Explorer 5 для о т о б р а ж е н и я стандартного вывода придется


выполнить более сложную последовательность действий. Сначала в меню Tools надо
выбрать Internet Options, активизировать вкладку Advanced, а в разделе Java VM вклю­
чить опцию J a v a console enabled. После этого необходимо перезапустить Internet
Explorer, а затем выбрать в меню View пункт J a v a Console; в результате на экране поя­
вится окно, в котором будет отображаться и н ф о р м а ц и я , выводимая в стандартный
выходной поток. Ч т о б ы получить список допустимых команд, надо задать символ "?".
В Internet Explorer 4 отображение окна J a v a Console разрешено по умолчанию.

Отображение стандартного выходного потока в appletviewer


Если a p p l e t v i e w e r выполняется в системе Windows или UNIX, выходной поток
выводится в том окне, из которого a p p l e t v i e w e r был запущен. В Мае для этой цели
используется специальное окно.
318 Глава 9. Аплеты и основные действия с графикой

9.4. Жизненный цикл аплета


Класс Applet отличается от других классов тем, что броузер автоматически созда­
ет его экземпляр и вызывает его методы в определенной последовательности. Бро­
узер никогда не обращается к main. Вместо этого он вызывает перечисленные ниже
методы. В классе A p p l e t в теле этих методов не содержится ни одной команды, все
необходимые действия реализует автор аплета.

public void init()


Этот метод вызывается сразу же после создания броузером экземпляра аплета. В
Netscape после остановки и перезапуска аплета метод i n i t повторно не вызывает­
ся. Исключение составляют те случаи, когда аплет удаляется броузером из памяти.
В Internet Explorer i n i t вызывается всякий раз, когда пользователь возвращается
к странице, содержащей аплет.

public void start()


Метод s t a r t вызывается после завершения i n i t и перед первым вызовом p a i n t .
Метод s t a r t также вызывается при каждом повторном запуске аплета после его
остановки. Если пользователь переходит к какому-либо окну в системе, а затем
возвращается к окну, содержащему аплет, возобновить анимационную последова­
тельность удобнее всего в теле метода s t a r t . Netscape вызывает s t a r t и s t o p
при изменении размеров окна броузера; Internet Explorer, a p p l e t v i e w e r и
Hotjava не делают этого.

public void paint(Graphics g)


Обычно в тело метода p a i n t пользователь включает команды, выполняющие ри­
сование. Данный метод вызывается броузером после завершения методов i n i t и
s t a r t , кроме того, броузер обращается к методу p a i n t всякий раз, когда считает,
что возникла необходимость обновить содержимое окна. Обычно это происходит
тогда, когда часть области, выделенной для аплета, на некоторое время перекры­
вается другим окном, а затем окно снова становится активным. Если вы хотите об­
новить содержимое области, занимаемой аплетом, вы должны вызвать метод
r e p a i n t ; в этом случае в потоке, занимающемся отображением графики, при пер­
вой возможности будет вызван метод p a i n t .

public void stopO


Броузер вызывает метод s t o p тогда, когда страница, содержащая аплет, становит­
ся неактивной. Часто этот метод используется для остановки анимационной по­
следовательности, которая затем возобновляется в теле метода s t a r t . В Netscape
метод s t o p также вызывается при изменении размеров окна броузера.

public void destroyO


Метод d e s t r o y вызывается при "разрушении" аплета (т.е. тогда, когда аплет завер­
шает работу либо когда броузер выгружает аплет из памяти, чтобы не допустить не­
оправданного увеличения размеров образа программы). Этот метод разработчики
9 . 5 . Прочие методы аплета 319

используют сравнительно редко; в некоторых случаях в теле d e s t r o y освобождают


совместно используемые ресурсы. В Internet Explorer метод d e s t r o y вызывается
каждый раз, когда страница, содержащая аплет, становится неактивной.
Внимание!

В отличие от Navigator, в Internet Explorer метод destroy вызывается


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

9.5. Прочие методы аплета


Варианты методов, описанных в предыдущем разделе, реализованные в классе
Applet, не выполняют никаких действий; предполагается, что разработчик аплета
должен переопределить их. Кроме того, класс A p p l e t содержит ряд методов общего
назначения. Некоторые из них, используемые чаще других, перечислены ниже. За­
метьте, что класс A p p l e t является подклассом классов Panel, C o n t a i n e r и
Component. Подробно эти классы будут рассмотрены в главе 13.

public void add(Component с)


public void add(Component с, Object constraints)
public void add(String location, Component c)
public void add(PopupMenu menu)
Эти методы включают графические компоненты в окно аплета. Они будут обсуж­
даться в главах 12 и 13.

public boolean contains(int х, int у)


public boolean contains(Point p)
Метод c o n t a i n s определяет, содержится ли точка с указанными координатами в
области, занимаемой аплетом. Он возвращает значение t r u e , если координата х
меньше или равна ширине аплета, а координата у меньше или равна высоте аплета.

public Image createlmage(int width, int height)


public Image createImage(ImageProducer producer)
Метод c r e a t e Image используется для создания карты пикселей без вывода ее на
экран. Подобно g e t Image, c r e a t e Image нельзя вызывать до выполнения метода
i n i t . Таким образом, этот метод не может быть использован для инициализации
переменных экземпляра.

public String getAppletInfo()


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

public AudioClip getAudioClip(URL audioFile)


public AudioClip getAudioClip(URL base, String audioFilename)
Эти методы копируют аудио- или MIDI-файлы с удаленных узлов и связывают их с
объектами AudioClip. Класс AudioClip содержит методы p l a y , loop и s t o p .
JDK 1.1 поддерживает только формат . аи. В JDK 1.2 реализована поддержка ау­
диофайлов . a i f f и .wav, а также форматов MIDI Туре О, MIDI Туре 1 и RMF.

public Color getBackground()


public void setBackground(Color bgColor)
Данные методы позволяют устанавливать цвет фона аплета, а также получать ин­
формацию о нем. Для создания класса Color используется следующий вызов кон­
структора:
Color someColor = new C o l o r ( r e d , green, blue);
Параметры red, g r e e n и b l u e представляют собой целые в интервале от О до 255
либо числа с плавающей точкой в интервале от 0.0 до 1.0. Класс Color также мо­
жет быть создан с помощью метода C o l o r . getHSBColor (hue, s a t u r a t i o n ,
b r i g h t n e s s ) , параметрами которого являются числа с плавающей точкой в диа­
пазоне от 0.0 до 1.0. Кроме того, для определения некоторых цветов существуют
константы Color.black, Color.blue. Color.cyan. Color.darkOray,
Color.gray. C o l o r . g r e e n , C o l o r . l i g h t G r a y , Color.magenta, Color.orange.
Color.pink. Color.red, Color.white и Color.yellow.
Класс SystemColor предоставляет доступ к цветам, используемым в системе.
Применение этого класса позволяет создавать аплет, внешний вид которого соответ­
ствует текущей цветовой схеме. Так, например, перед выполнением d r a w S t r i n g в
теле метода p a i n t можно вызвать д. s e t C o l o r (SystemColor .windowText). Цвета,
используемые при работе системы, перечислены в табл. 9.1.

Таблица 9 . 1 . Системные цвета

Цвет (статическая Описание


переменная класса
Sys temCol or)
activeCaption Цвет фона для заголовка активного окна
activeCaptionBorder Цвет обрамления для заголовка активного окна
control Цвет фона управляющих объектов
controlDkShadow "Густая" тень, используемая для создания трехмер­
ных эффектов
controlHighlight Цвет выделения
controlLtHighlight Цвет слабого выделения
controlShadow "Легкая" тень, используемая для создания трехмер­
ных эффектов
9.5. Прочие методы аплета 321

Оконаниетабл. 9.1
controlText Цвет для вывода текста
desktop Цвет ф о н а для рабочего стола
inactiveCaption Ц в е т ф о н а для заголовков неактивных окон
inactiveCaptionBorder Ц в е т обрамления для заголовков неактивных окон
inactiveCaptionText Цвет для вывода текста в заголовках неактивных окон
info Ц в е т фона для вывода вспомогательного текста
(подсказок)
infoText Цвет вспомогательного текста (подсказок)
menu Цвет фона для невыбранных пунктов меню. Для вы­
бранного пункта используется t e x t H i g h l i g h t
menuText Цвет для вывода текста невыбранных пунктов меню.
Для выбранного пункта используется
textHighlightText
scrollbar
Цвет ф о н а для полос прокрутки
text
Ц в е т ф о н а для вывода текстовых компонентов
textHighlight Цвет фона для вывода "подсвеченного" текста.
"Подсвеченным" считается выделенный текст в по­
лях редактирования, выбранные пункты меню и
п р о ч и е элементы
textHighlightText Цвет "подсвеченного" текста
textlnactiveText Цвет текста для неактивных компонентов
textText Цвет текста для текстовых компонентов
window Ц в е т ф о н а для окон
windowBorder Цвет обрамления для окон
windowText Цвет текста, отображаемого в окнах

Класс S y s t e m C o l o r дает возможность непосредственно определять системные


цвета для настройки внешнего вида аплета. Если вы используете при создании аплета
компоненты Swing, то можете легко сконфигурировать компоненты в соответствии с
определенной системой, вызывая методы класса U I M a n a g e r . В частности, разработ­
чику доступны элементы в стилях Motif, Windows, Mac и Java (Metal). Подробно ис­
пользование компонентов Swing в аплетах будет рассмотрено в главе 14.

public U R L getCodeBaseO
public U R L g e t D o c u m e n t B a s e O
Эти методы возвращают и н ф о р м а ц и ю о расположении аплета ( g e t C o d e B a s e ) и
HTML-документа, содержащего аплет ( g e t D o c u m e n t B a s e ) .
322 Глава 9. Аплеты и основные действия с графикой

p u b l i c C o m p o n e n t g e t C o m p o n e n t A t ( i n t х, int у)
Метод g e t C o m p o n e n t A t возвращает "самый верхний" из компонентов, располо­
женных в указанной позиции. Это может быть либо сам аплет, либо компонент,
включенный в его состав. Если точка (х, у) находится за пределами области, выде­
ленной для аплета, данный метод возвращает значение n u l l .

p u b l i c Cursor getCursor()
public void setCursor(Cursor cursor)
Эти методы позволяют прл)^чать и устанавливать курсор.

public Font getFontO


public void s e t F o n t ( F o n t defaultFont)
Данные методы дают возможность устанавливать по умолчанию ш р и ф т для аплета
и получать и н ф о р м а ц и ю об используемом ш р и ф т е . Ш р и ф т по умолчанию приме­
няется для отображения статического текста, кнопок, символов в полях редакти­
рования и других компонентов, содержащихся в аплете, а также для вывода строк
посредством метода d r a w S t r i n g объекта G r a p h i c s . Ш р и ф т ы создаются с помо­
щью конструктора класса F o n t , которому передаются имя шрифта, стиль и раз­
мер, например:
String family = "Serif";
int style = Font.BOLD;
int size = 18;
Font font = new Font(family, style, size);
setFont(font) ;
BJDK 1.1 разрешено указывать ш р и ф т ы S e r i f , S a n s S e r i f , M o n o s p a c e d , D i a l o g
и D i a l o g l n p u t . В качестве идентификатора стиля могут быть заданы F o n t .
PLAIN, F o n t . BOLD, F o n t . ITALIC или F o n t . BOLD | F o n t . ITALIC. Размер задается
целым числом. Использование нестандартных ш р и ф т о в в JDK 1.1 не допускается,
даже если они инсталлированы на локальной машине. B J D K 1.2 локальные шриф­
ты могут быть доступны, для этого надо вызвать метод g e t A v a i l a b l e F o n t -
F a m i l y N a m e s или g e t F o n t s класса G r a p h i c s E n v i r o n m e n t . Подробнее исполь­
зование локальных ш р и ф т о в будет рассмотрено в главе 10.

public F o n t M e t r i c s g e t F o n t M e t r i c s ( F o n t f)
Данный метод возвращает объект, который может быть использован для опреде­
ления размеров строк ( s t r i n g W i d t h ) или отдельных символов ( c h a r W i d t h ) ,
представленных определенным шрифтом.

public Color g e t F o r e g r o u n d O
public Color s e t F o r e g r o u n d ( C o l o r fgColor)
Эти методы позволяют определять и устанавливать цвет переднего плана, исполь­
зуемый в аплете по умолчанию. Способы задания цвета были рассмотрены при об­
суждении метода g e t B a c k g r o u n d .
9.5. Прочие методы аплета 323

p u b l i c Graphics getGraphics()
Метод g e t G r a p h i c s () возвращает текущий графический объект аплета. Этот ме­
тод используется в тех случаях, когда необходимо выполнить операции рисования
за пределами метода p a i n t . Метод p a i n t получает графический объект автома­
тически.

p u b l i c I m a g e g e t I m a g e ( U R L imageFile)
public I m a g e g e t I m a g e ( U R L base, String imtageFilename)
Варианты метода g e t Image "регистрируют" удаленные файлы изображений и
связывают их с объектом I m a g e . Исполняющая система Java не загружает изобра­
жение до тех пор, пока не возникнет необходимость отобразить его либо пока не
будет получено обращение с требованием начать загрузку (для этого используется
p r e p a r e Image или M e d i a T r a c k e r ) . Подробнее о выводе изображений рассказы­
вается ниже в этой главе.

p u b l i c Locale getLocale()
p u b l i c v o i d setLocale(Locale locale)
Эти методы позволяют определять и устанавливать текущую национальную коди­
ровку.

p u b l i c String getParameter(String p a r a m e t e r N a m e )
Метод g e t P a r a m e t e r позволяет определять значения параметров, передаваемых
с помощью дескрипторов PARAM. Д е с к р и п т о р ы РАКАМ включаются в состав деск­
риптора APPLET HTML-документа. Подробно передача параметров будет рассмат­
риваться далее в этой главе.

p u b l i c String[ ][ ] getParameterInfo()
Данный метод предоставляет описание параметров, распознаваемых аплетом.
Каждый элемент массива, в свою очередь, является массивом, содержащим имя
параметра, его тип и краткое описание.

p u b l i c Container getParent()
Метод g e t P a r e n t возвращает родительское окно либо значение n u l l .

public D i m e n s i o n getSize()
Метод g e t S i z e возвращает объект D i m e n s i o n , который описывает размеры об­
ласти, занимаемой аплетом. Этот объект содержит поля w i d t h и h e i g h t . Таким
образом, ширина области, выделенной для аплета, определяется с помощью вы­
ражения g e t S i z e () . w i d t h . В классе A p p l e t также определен метод s e t S i z e () ,
но на практике большинство броузеров игнорирует этот метод; размеры области
задаются с помощью атриб)Т'ов WIDTH и HEIGHT дескриптора APPLET и не изме­
няются в процессе работы аплета. Исключением является броузер a p p l e t v i e w e r .
324 Глава 9. Аплеты и основные действия с графикой

p u b l i c b o o l e a n isActive()
Данный метод сообщает, является ли аплет активным. Аплет считается неактив­
ным до вызова метода s t a r t и после вызова метода s t o p .

public void play(URL audioFile)


public void play(URL base, String a u d i o F i l e n a m e )
Данные методы копируют и воспроизводят аудиофайлы формата аи. В JDK 1.2,
кроме того, поддерживаются ф а й л ы . a i f f n .wav, а также ф о р м а т ы MIDI Туре О,
MIDI Туре 1 и RMF.

p u b l i c void repaint()
p u b l i c void repaint(long m i l l i s e c o n d D e l a y )
public void repaint(int x, int y, int width, int height)
public void repaint(long m s D e l a y , int x, int y, int width, int height)
Метод r e p a i n t дает команду потоку AWT вызывать метод u p d a t e либо немедлен­
но, либо через заданное число миллисекунд. В любом случае r e p a i n t сразу же
возвращает управление вызывающему методу, а обновление и н ф о р м а ц и и на экра­
не осуществляется в отдельном потоке. Вы также можете указать методу на то, что
обновлению подлежит лишь часть экрана. В этом случае методы u p d a t e и p a i n t
получат объект G r a p h i c s с установленной областью отсечения.

p u b l i c void s h o w D o c u m e n t ( U R L h t m l D o c ) [в классе A p p l e t C o n t e x t ]
public void s h o w D o c u m e n t ( U R L h t m l D o c , String f r a m e N a m e ) [в классе
AppletContext]
Д а н н ы й метод дает команду броузеру скопировать и отобразить Web-страницу.
Реально этот метод принадлежит не классу A p p l e t , а классу A p p l e t C o n t e x t ,
но используется подобно другим описанным выше методам. К данному методу не­
обходимо обращаться с помощью выражения g e t A p p l e t C o n t e x t ( ) . s h o w -
Document { . . . ) • Броузер a p p l e t v i e w e r игнорирует метод showDocument.

p u b l i c v o i d showStatus(String m e s s a g e )
Метод s h o w S t a t u s отображает последовательность символов в строке состояния,
расположенной в нижней части окна броузера.

p u b l i c void u p d a t e ( G r a p h i c s g)
Этот метод вызывается потоком AWT после вызова метода r e p a i n t . Реализация
r e p a i n t по умолчанию очищает экран, а затем вызывает метод p a i n t . В прило­
жениях, обрабатывающих анимационные последовательности и выполняющих
двойную буферизацию, данный метод обычно переопределяется так, чтобы он вы­
зывал метод p a i n t , но не очищал экран. Данный вопрос будет подробно обсуж­
даться в главе 16.
9 . 6 . Э л е м е н т APPLET 325

a d d C o m p o n e n t L i s t e n e r , a d d F o c u s L i s t e n e r , addKeyListener,
andMouscListener, addMouseMotionListener
Эти общедоступные методы добавляют к аплету обработчики различных событий.
Каждому из методов addXxAjListener соответствует метод r e m o v e X X X L i s t e n e r .
Эти методы будут обсуждаться в главе 10.

9.6. Элемент APPLET


Элемент: <APPLET CODE="...' WIDTH=xxx HEIGHT=xxx ...> ...
</APPLET>
Атрибуты: CODE, WIDTH (обязательный), HEIGHT (обязательный), CODEBASE, ALT, ALIGN,
HSPACE, VSPACE, NAME, OBJECT, ARCHIVE (нестандартный), MAYSCRIPT (нестандартный)
Элемент APPLET связывает файл класса с Web-страницей. Соответствующий класс
должен быть подклассом A p p l e t . Необходимо, чтобы в составе дескриптора APPLET
присутствовали атрибут CODE либо OBJECT, а также атрибуты WIDTH и HEIGHT.

CODE
П р и отсутствии атрибута OBJECT значением CODE должно быть имя файла класса.
В атрибуте CODE нельзя указывать абсолютный URL. Файл класса должен нахо­
диться в том же каталоге, что и HTML-документ, либо в каталоге, на который ука­
зывает атрибут CODEBASE. Файл класса должен быть дост)^пен из Web; на исходный
Java-файл это требование не распространяется.

На з а м е т к у

Значением атрибута CODE не может быть абсолютный URL. Если ап- ^


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

WIDTH и HEIGHT
Атрибуты WIDTH и HEIGHT определяют размеры области, выделяемой для аплета.
Значения атрибутов могут быть заданы в пикселях либо в процентах от ширины ок­
на броузера. Броузер a p p l e t v i e w e r не поддерживает значения в процентах, по­
скольку для этого броузера понятие ширины окна не существует. Атрибуты WIDTH и
HEIGHT должны обязательно присутствовать в составе дескриптора <APPLET>.

Внимание!

Броузер appletviewer не поддерживает значения атрибутов WIDTH и


HEIGHT, заданные в процентах.
326 Глава 9. Аплеты и основные действия с графикой

CODEBASE
Данный атрибут определяет URL каталога, в котором содержится аплет. Имя ап-
лета задается с помощью атрибута CODE. Если CODEBASE отсутствует, считается,
что аплет расположен в том же каталоге, что и соответствующий HTML-документ.

ALT
Броузеры, поддерживающие Java, игнорируют текстовую информацию, содержа­
щуюся между дескрипторами <APPLET . . . > и </APPLET>, поэтому здесь удобнее
всего помещать альтернативный текст. Атрибут ALT применяется крайне редко.

ALIGN
Этот атрибут определяет тип выравнивания области, выделенной для аплета. Зна­
чения данного атрибута (LEFT, RIGHT, TOP, BOTTOM, MIDDLE) совпадают со значе­
ниями атрибута ALIGN дескриптора IMG и интерпретируются так же.

HSPACE
Атрибут HSPACE задает размеры (в пикселях) пустого пространства слева и справа
от аплета.

VSPACE
Атрибут VSPACE задает размеры (в пикселях) пустого пространства сверху и снизу
от аплета.

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

Методика профессионалов
Значением атрибута NAME дескриптора APPLET должно быть имя, со­
держащее лишь символы нижнего регистра.

OBJECT
Атрибут OBJECT указывает на аплет, который был сохранен средствами сериали-
3a4HHjava.

ARCHIVE
Атрибут ARCHIVE задает архив файлов классов, которые должны быть загружены
перед выполнением аплета. Архив необходимо представить в формате Java
ARchive ( . j a r ) . Netscape 3.01 поддерживает также несжатые ZIP-архивы ( . z i p ) .
9.7. Чтение параметров 327

Кроме файлов классов в JAR-файл могут быть включены изображения, используе­


мые аплетом. Например, если аплету J a v a M a n l , c l a s s требуется изображение
J a v a M a n . g i f , вы можете упаковать соответствующие файлы в архив J a v a M a n .
j а г и указать имя JAR-файла в дескрипторе APPLET следующим образом:
<APPLET CODE="JavaManl.class"
ARCHIVE="JavaMan.jar"
WIDTH=375 HEIGHT=370>
<B>Sorry, you have a Java-challenged browser. </B>
</APPLET>
Подробнее о создании JAR-файл ов см. в разделе 7.10.

MAYSCRIPT
Netscape и Internet Explorer используют данный атрибут для того, чтобы указать,
разрешено ли управление аплетом изJavaScript-cцeнapия.

9.7. Чтение параметров


Элемент: <PARAM NAME="..." VALUE="..."> (закрывающий
дескриптор отсутствует)
Атрибуты: NAME (обязательный), VALUE (обязательный)
Как вы уже знаете, методу m a i n приложения параметры передаются в массиве
S t r i n g [ ] . В отличие от приложений, аплет не получает такой массив. П р и вызове
аплета параметры передаются ему с помощью дескриптора <PARAM>, который поме­
щается между дескрипторами <APPLET . . .> и </APPLET>. Формат дескриптора
<PARAM> имеет елед)тощий вид:
<PARAM NAME="Parameter Name" VALUE="Parameter Value">
В теле аплета для чтения параметров используется метод g e t P a r a m e t e r . П р и вы­
зове этого метода ему передается имя параметра. В результате выполнения метода
возвращается объект S t r i n g либо, если параметр не найден, значение n u l l . Не­
смотря на то что имена HTML-элементов и атрибутов не зависят от регистра симво­
лов, при передаче имени параметра методу g e t P a r a m e t e r регистр должен учиты­
ваться. Для сравнения строковых значений нельзя применять оператор ==, поскольку
он лишь проверяет, являются ли две строки одним и тем же объектом. Для сравнения
строк можно использовать метод e q u a l s (сравнение с учетом регистра символов) ли­
бо метод e q u a l s I g n o r e C a s e (сравнение без учета регистра) класса S t r i n g .
Несмотря на то что метод g e t P a r a m e t e r возвращает объект S t r i n g , строку сим­
волов можно преобразовать в целочисленное значение с помощью метода p a r s e I n t
класса I n t e g e r . Н и ж е будет приведен пример использования метода I n t e g e r ,
p a r s e I n t , а также перечислены методы для преобразования строк в значения b y t e ,
s h o r t , l o n g , f l o a t и double.
Приступая к разработке аплета, следует помнить, что имена параметров WIDTH и
HEIGHT замещают значения атрибутов WIDTH и HEIGHT дескриптора <APPLET>, по­
этому применять их не рекомендуется.
328 Глава 9 . Аплеты и основные д е й с т в и я с графикой

Внимание!

Не следует использовать WIDTH И HEIGHT В качестве имен парамет­


ров, передаваемых аплетам.

Пример чтения параметров аплетом


в листинге 9.5 представлен пример аплета HelloWWW, который рассматривался в
главе 6. Этот аплет получает из HTML-документа параметр, передаваемый с помощью
дескриптора <PARAM>:
<PARAM NAME="BACKGROUND" VALUE="LIGHT">
ИЛИ

<PARAiyi NAME="BACKGROUND" VALUE="DARK">


Обратите внимание, что в теле аплета выполняется проверка на равенство
b a c k g r o u n d T y p e значению n u l l . Это значение возвращается в тех случаях, когда де­
скриптор <PARAM> отсутствует, либо если значение атрибута NAME отличается от
"BACKGROUND" (с учетом регистра символов). П р и отсутствии такой проверки про­
грамма может выполняться некорректно. В частности, если g e t P a r a m e t e r возвра­
щает значение n u l l , обращение b a c k g r o u n d T y p e . e q u a l s ( . . . ) недопустимо, по­
скольку в объекте n u l l нет метода e q u a l s , равно как и других методов. Возникнове­
ние ошибки можно предотвратить, выполняя сравнение следующим образом:
if ("LIGHT".equals(backgroundType))
вместо
if (backgroundType.equals("LIGHT"))
HO многие разработчики предпочитают явно проверять переменную на равенство
null.
М е т о д и к а профессионалов

Создавая аплет, обязательно предусматривайте обработку ситуа­


ции, при которой параметр не найден,

В листинге 9.6 представлен код HTML-документа, в котором один и тот же аплет


загружается три раза с различными параметрами. Внешний вид документа в окне бро­
узера Netscape 4.08, выполняющегося в среде Windows 98, показан на рис. 9.3.

Листинг 9 . 5 . HelloWWW2.java

import Java.applet.Applet;
import java.awt.*;

p u b l i c c l a s s HelloWWW2 e x t e n d s A p p l e t {
public void i n i t O {
s e t F o n t ( n e w F o n t ( " S a n s S e r i f " , Font.BOLD, 30));
9.7. Чтение параметров 329

Color background = Color.gray;


Color foreground = Color.darkGray;
String backgroundType = getParameter("BACKGROUND");
if (backgroundType != null) {
if (backgroundType.equalsIgnoreCase("LIGHT")) {
background = Color, whiter-
foreground = Color.black;
} else if (backgroundType.equalsIgnoreCase("DARK")) {
background = Color.black;
foreground = Color.white;
}
}
setBackground(background);
setForeground(foreground);

public void paint(Graphics g) {


g.drawString("Hello, World Wide Web.", 5, 35)
}
}

Листинг 9.6. HelloWWW2 . html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Customizable HelloWWW Applet</TITLE>
</HEAD>
<BODY>
<Hl>Customizable HelloWWW Applet</Hl>
<P>
<APPLET C0DE="HelloWWW2.class" WIDTH=400 HEIGHT=40>
<PARAM NAME="BACKGROUND" VALUE="LIGHT">
<B>Error! You must use a Java-enabled browser.</B>
</APPLET>
<P>
<APPLET C0DE="HelloWWW2.class" WIDTH=400 HEIGHT=40>
<PARAM NAME="BACKGROUND" VALUE="DARK">
<B>Error! You must use a Java-enabled browser.</B>
</APPLET>
<P>
<APPLET C0DE="HelloWWW2.class" WIDTH=400 HEIGHT=40>
<B>Error! You must use a Java-enabled browser.</B>
</APPLET>
</BODY>
</HTML>
330 Глава 9. Аплеты и основные действия с графикой

шг аЫе HHkrWWWAn^et Netscm» S^


^' / 'J r^* ^ й 3 rf 3 il
Customizable Hello WWW Applet
Hello, World Wide Web.
Hello. World Wide Web.

Ш^^ШШ
:^ "Ф^

Рис. 9.3. Элементы PARAM используются в HTML-


документах для передачи параметров аплетам

9-8. HTML-элемент OBJECT


Элемент: <OBJECT CLASSID="..." ... > ... </OBJECT>
А т р и б у т ы : CLASSID, CODETYPE, CODEBASE, STANDBY, WIDTH, HEIGHT, NAME, ALIGN,
HSPACE,VSPACE
Может показаться странным, но согласно спецификации HTML 4.0, элемент
APPLET не рекомендован для использования. Предполагается, что разработчики бу­
дут применять более универсальный элемент OBJECT. Несмотря на т о что элемент
OBJECT поддерживает разнообразные типы объектов (например, ActiveX, Applet,
QuickTime), многие авторы все же предпочитают включать аплеты в HTML-докумен­
ты с помощью дескрипторов <APPLET>. П р и использовании дескриптора <APPLET> в
начало документа должна быть включена промежуточная (Transitional) декларация
DOCTYPE. Для элемента OBJECT данное требование отсутствует.
В спецификации HTML 4.0 для элемента OBJECT предусмотрено 32 атрибута, кро­
ме того, броузеры продолжают поддерживать 6 атрибутов, не рекомендованных для
использования. Н и ж е представлены некоторые из атрибутов, наиболее часто приме­
няемых при работе с аплетами. Подробно об элементе OBJECT см. в главе 3.

CLASSID
Задает URL объекта. Для аплетов значение CLASSID представляется в ф о р м е
Java:Applet.class.

CODETYPE
Атрибут CODETYPE определяет тип объекта, предназначенного для копирования.
Для аплетов используется значение C O D E T Y P E = " a p p l i c a t i o n / J a v a " .

CODEBASE
Данный атрибут указывает URL каталога, в котором расположен объект (аплет).
9.8. HTML-элемент OBJECT 331

STANDBY
Атрибут STANDBY задает строку, которая отображается в процессе загрузки аплета.

WIDTH, HEIGHT, NAME, ALIGN, HSPACE и VSPACE


Данные атрибуты используются так же, как и одноименные атрибуты элемента
APPLET. Согласно спецификации HTML 4.0, атрибуты ALIGN, HSPACE и VSPACE не
рекомендованы для применения.
Описывая аплет с помощью элемента OBJECT, следует указать посредством атри­
бута CODETYPE тип Java и в качестве значения атрибута CLASS ID задать имя файла
класса. Если аплет и HTML-документ находятся в разных каталогах, в состав дескрип­
тора OBJECT следует добавить атрибут CODEBASE. П р и м е р описания аплета с помо­
щью элемента OBJECT показан в листинге 9.7.

Л и с т и н г 9 . 7 . HelloWWWObject.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">


<HTML>
<HEAD>
<TITLE>A HelloWWW Object</TITLE>
</HEAD>

<BODY>
<H1>A HelloWWW Object</Hl>

<OBJECT CODETYPE="application/java"
CLASSID="java:HelloWWW.class"
CODEBASE="applets"
WIDTH=400 HEIGHT=55>
<PARAM NAME="codebase" value="applets">
<PARAM NAME="code" value="HelloWWW.class">
<PARAM NAME="BACKGROUND" VALUE="DARK">
<B>Error! You must use a Java-enabled browser.</B>
</OBJECT>

</BODY>
</HTML>

Если файл класса и HTML-документ находятся в различных каталогах, Internet


Explorer и a p p l e t v i e w e r некорректно обрабатывают значение атрибута CODEBASE.
Поэтому при описании аплета с помощью элемента OBJECT принято задавать пара­
метр с именем CODEBASE. Кроме того, a p p l e t v i e w e r ожидает, что имя файла класса
должно быть задано с помощью атрибута CODE. Чтобы разрешить эту проблему, мож­
но добавить к элементу OBJECT параметр с именем CODE, значение которого повто­
ряло бы значение атрибута CLASS ID, например:
<PARAM NAME="code" v a l u e = " H e l l o W W W . c l a s s " >
332 Глава 9 . Аплеты и основные д е й с т в и я с графикой

М е т о д и к а профессионалов

Для того чтобы аплет корректно загружался в Internet Explorer и


appletviewer, полезно добавить к элементу OBJECT параметры с
именами CODE И CODEBASE.

9.9. Java Plug-In


Будучи инсталлированным, прод}'кт Java Plug-In (http://java.sun.com/
p r o d u c t s / p l u g i n / ) позволяет броузеру запускать текущую версию JRE (Java Runtime
Environment— среда выполнения Java). Благодаря этому разработчик аплета может
ориентироваться на современный вариант платформы Java, выполняемый как в среде
Netscape, так и в среде Internet Explorer. П р и этом разработчик перестает зависеть от
производителя броузера. Даже если производитель не включил в состав своего про-
д)^кта последнюю версию Java, аплеты могут запускаться в нем. Однако использование
Java Plug-In создает также определенные трудности.
Во-первых, размеры Java 2 Plug-In составляют около 5 Мбайт. Поэтому, если кли­
ент подключен через Internet, копирование Plug-In для конкретного аплета недопус­
тимо, особенно, если пропускная способность линии составляет 56,6 К или ниже. Од­
нако в сетях intranet использование Java Plug-In вполне оправдано. Для этого доста­
точно поместить продукт Plug-In, предназначенный для копирования на клиентские
машины, на сервер в локальной сети.

М е т о д и к а профессионалов

Не используйте Java Plug-In с аплетами, предназначенными для копи­


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

Во-вторых, процедуры вызова Java Plug-In в Netscape и Internet Explorer различают­


ся. Для использования в Internet Explorer дескриптор <APPLET> должен быть преобра­
зован в дескриптор <OBJECT>; при работе с Netscape элемент APPLET преобразуется в
элемент EMBED. Для автоматического преобразования элементов APPLET Sun предлага­
ет HTML-конвертер, находящийся по адресу h t t p : / / j a v a . s u n . c o m / p r o c i u c t s /
plugin/1.3/features.html.
Java Plug-In HTML-конвертор, окно которого показано на рис. 9.4, представляет
собой обычную Java-программу, работающую с различными шаблонами, ориентиро­
ванными на разные платформы. Версия 1.3 Java Plug-In HTML Converter поддержива­
ет следующие преобразования.

• "Standard (IE 8с Navigator) for Windows and Solaris only" — стандартное для
Windows и Solaris.
• "Extended (standard + all browsers/platforms)"— расширенное (стандартное +
все б р о у з е р ы / п л а т ф о р м ы ) .
9 . 9 . Java Plug-In 333

• "Internet Explorer for Windows and Solaris only" — Internet Explorer для Windows
и Solaris.
• "Navigator for Windows only" — Navigator для Windows.
Кроме того, для выполнения преобразования вы можете определять собственные
шаблоны.
*izJiii
Fee Edit Н ^

•C\CWP2\Java-Applets+Graphics-Code

*htrril, *htrn,* asp

indude Subfolders

с lCvvF2\java-Applets<-Graphics-Code\Backup

I Standard (IE & Navigator) for Windows & Solaris Only

Рис. 9.4. Java Plug-In HTML Converter, Version 1.3

При работе конвертер создает резервную копию HTML-файла, а затем разбирает


документ, преобразуя каждый элемент APPLET в OBJECT или EMBED, в зависимости от
выбранного броузера или шаблона. В листинге 9.8 показан элемент APPLET перед
преобразованием его с помощью Java Plug-In HTML Converter. Получившиеся в ре­
зультате элементы-контейнеры показаны в листинге 9.9 ("Navigator for Windows
Only") и 9.10 ("Internet Explorer for Windows 8c Solaris Only").

Листинг 9 . 8 . Дескриптор <APPLET> перед преобразованием с помощью


конвертера

<APPLET CODE="HelloWWW.class" CODEBASE="applets"


WIDTH=400 HEIGHT=40>
<PARAM NAME="BACKGROUND" VALUE="DARK">
< B > E r r o r ! You m u s t u s e a J a v a - e n a b l e d b r o w s e r . < / B >
</APPLET>

Листинг 9 . 9 . Результат преобразования "Navigator for Windows Only"

<EMBED type="application/x-java-applet;version=l.3"
CODE = "HelloWWW.class" CODEBASE = "applets"
WIDTH = 400 HEIGHT = 40
BACKGROUND = "LIGHT"
scriptable=false
pluginspage="http://Java.sun.com/products/plugin/1.3/
334 Глава 9. Аплеты и основные действия с графикой

plugin-install.html"
>
<NOEMBED>
< B > E r r o r ! You m u s t u s e a J a v a - e n a b l e d browser.</B>
</NOEMBED>
</EMBED>

Листинг 9 . 1 0 . Результат преобразования "Internet Explorer for Windows &


Solans Only"

<OBJECT classid="clsid:8AD9C840-044E-llDl-B3E9-00805F499D93'
WIDTH = 400 HEIGHT = 40
Godebase="http://Java.sun.com/products/plugin/1.3/
jinstall-13-win32.cab#Version=l,3,0,0"
>
<PARAM NAME = CODE VALUE = "HelloWWW.class" >
<PARAM NAME = CODEBASE VALUE = "applets" >
<PARAM NAME="type"
VALUE="application/x-java-applet;version=l.3">
<PARAM NAME=="scriptable" VALUE="false">
<PARAM NAME = "BACKGROUND" VALUE ="LIGHT">
<B>Error! You must use a Java-enabled browser.</B>
</OBJECT>

Из этих листингов видно, что HTML-конвертер добавляет ссылку для копирования


Java Plug-In. Копирование выполняется в том случае, если продукт не установлен на
клиент-машине. Для использования в сетях intranet Java Plug-In сначала копируется с
сервера Sun, а затем размещается в локальной сети с большой пропускной способно­
стью соединений. После того как требуемые программы помеш^ены на сервере intranet,
ссылка в контейнере аплета изменяется в соответствии с новым расположением аплета.

9.10. Графические приложения


В предыдущих примерах рассматривались аплеты — Java-программы, выполняю­
щиеся в среде броузера. Локальные Java-программы также могут использовать окна.
Независимое графическое Java-приложение можно создать как подкласс J F r a m e , ко­
торый представляет собой "тяжеловесный" Swing-компонент. Приложения сущест­
венно отличаются от аплетов; одним из отличий является тот факт, что аплеты реко­
мендуется создавать, используя AWT-компоненты. П р и ч и н а в том, что большинство
броузеров не обеспечивает полной поддержки новых Swing-компонентов (если в них
не установлен продукт Java Plug-In либо если классы Swing не копируются по сети).
Java-приложение выполняется на рабочей станции клиента как независимый эк­
земпляр визуальной машины Java. Перед запуском приложения вы можете убедиться,
что на клиентском компьютере установлена версия виртуальной машины Java, под­
держивающая Swing-компоненты. Подробно платформенно-независимые компонен­
ты Swing будут описаны в главе 14, здесь же мы рассмотрим лишь основные вопросы
работы приложений с окнами и вывода графической информации.
9 . 1 1 . Графические операции 335

П р и создании Java-приложения на базе J F r a m e разработчик задает заголовок в


конструкторе класса, определяет ширину и высоту окна посредством метода
s e t S i z e , а затем отображает окно с помощью метода s e t V i s i b l e . Н и ж е приведено
несколько строк кода, которые представляют собой шаблон приложения, исполь­
зующего окно.
p u b l i c c l a s s MyFrame e x t e n d s JFrame {
JFrame frames-
public s t a t i c void main(String[] args) {
frame = new M y F r a m e ( " t i t l e " ) ;

frame.addWindowListener(new E x i t L i s t e n e r ( ) ) ;
frame.setSize(width, height);
frame.setVisible(true) ;
}

Одной из особенностей работы приложений является тот факт, что пользователь


не может закрыть основное окно программы, если соответствующее действие не пре­
дусмотрено при создании кода (на дочерние окна это ограничение не распространя­
ется). Поэтому к приложению обычно добавляется объект E x i t L i s t e n e r , которое
при возникновении события, связанного с закрытием окна, вызывает метод
S y s t e m . e x i t ( 0 ) . Например:
public class ExitListener extends WindowAdapter {
public void windowClosing(WindowEvent event) {
System.exit(0);
}
}
В главе 14 будут приведены дополнительные примеры создания Java-приложений.
Специальный класс W i n d o w U t i l i t i e s предоставляет вспомогательные методы, уп­
рощающие задачу создания окон.

9 . 1 1 . Графические операции
Традиционно в аплетах и приложениях для рисования используется метод p a i n t . В
качестве параметра этому методу передается объект G r a p h i c s . Другие методы могут
получить объект G r a p h i c s путем вызова g e t G r a p h i c s . Однако при создании аплета
или приложения не стоит вызывать метод g e t G r a p h i c s , а затем сохранять объект
G r a p h i c s в переменной экземпляра, поскольку при последующих вызовах методу
p a i n t буду!^ переданы другие экземпляры этого объекта. Методы, в которых осуществ­
ляется рисование, лучше вызывать из метода p a i n t и передавать им объект G r a p h i c s .
В аплетах и приложениях, использующих Swing, объект G r a p h i c s используется
для выполнения простых операций рисования. Основные действия, связанные с вы­
водом графической информации, выполняются не в методе p a i n t , а в методе
p a i n t C o m p o n e n t . Этот метод доступен лишь в "легковесных" Swing-компонентах, к
которым не относятся J A p p l e t и J F r a m e . Таким образом, для выполнения операций
рисования в Swing к J A p p l e t и J F r a m e добавляется "легковесный" компонент, чаще
всего J P a n e l . Более подробно операции рисования в аплетах и приложениях обсуж­
даются в главах 10 и 14.
336 Глава 9 . Аплеты и основные д е й с т в и я с графикой

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


и в таких контейнерах, как P a n e l , C a n v a s и Frame. Эти компоненты будут описаны в
главе 13. Кроме того, методы рисования могут применяться в "легковесных" Swing-
компонентах, например J P a n e l , однако в этом случае для рисования рекомендуется
использовать графические средства Java 2D.
В Java нет методов, позволяющих определять абсолютную позицию аплета в окне
броузера, однако расположение окна приложения можно выяснить. В методах, рас­
сматриваемых ниже, все координаты являются относительными. Точкой (0,0) счита­
ется верхний левый угол окна; координата х возрастает при движении вправо, а коор­
дината у— при движении вниз. Как и во многих других графических системах, в Java
считается, что точка, определенная парой координат, располагается между пикселя­
ми на экране. П р и рисовании контуров фигур закрашиваются пиксели, расположен­
ные справа и снизу от заданных координат. П р и заполнении ф и г ) р цветом закраши­
ваются пиксели, лежащие между указанными координатами. Это означает, что конту­
ры прямоугольника занимают в направлении вправо и вниз на один пиксель больше,
чем тот же прямоугольник, закрашенный цветом.
Объект AWT Graphics поддерживает простые операции рисования, но не поддержи­
вает ширину пера (толщину линии) и операции заполнения конт)ров. Однако в состав
платформы Java 2 включен графический пакет Java 2D API на базе объекта G r a p h i c s 2 D ,
который обеспечивает поддержку ширины пера, стилей линий (штриховые пунктир­
ные и др.) заполнения фигур, позволяет работать со шрифтами и обеспечивает многие
другие возможности. Дополнительную информацию о Java 2D вы найдете в главе 10 ли­
бо по адресу h t t p : / / J a v a , s u n , c o m / p r o d u c t s / j a v a - m e d i a / 2 D / .

М е т о д и к а профессионалов

Для выполнения простых операций рисования в аплетах используй­


те объект Graphics. Для вывода высококачественных графических
изображений следует применять объект Java 2D Graph±cs2D (для ко­
торого требуется Java Plug-In).

Н и ж е перечислены методы объекта G r a p h i c s .

Операции рисования
p u b l i c void clearRect(int left, int t o p , int width, int height)
Метод c l e a r Re c t отображает в указанной позиции закрашенный прямоугольник.

public void copyArea(int left, int t o p , int width, int height, int deltaX, int deltaY)
Данный метод копирует все пиксели из прямоугольника, определяемого парамет­
рами l e f t , t o p , w i d t h , h e i g h t , в прямоугольник l e f t + d e l t a X , t o p + d e l t a Y ,
width, height.

public Graphics create()


public Graphics create(int left, int t o p , int width, int height)
Данные! метод создает новый графический контекст. Если координаты прямо­
угольника указаны, контекст преобразуется к заданному расположению, а область
9 . 1 1 . Графические операции 337

отсечения устанавливается в соответствии с параметрами, определяющими ши­


рину и высоту.

public v o i d draw3DRect(int left, int t o p , int width, int h e i g h t , b o o l e a n raised)


Этот метод рисует вокруг заданного прямоугольника контур толщиной в 1 пиксель.
Если параметр r a i s e d равен t r u e , левая и верхняя границы отображаются более
светлыми, создавая иллюзию того, что прямоугольник "приподнят" над поверхно­
стью окна. Если значение r a i s e d равно f a l s e , левая и верхняя границы отобра­
жаются более темными, создавая впечатление того, что прямоугольник "вдавлен"
внутрь окна. Желательно, чтобы цвет переднего плана был установлен равным
цвету фона, поскольку цвет границ вычисляется на основе цвета фона.

public void fill3DRect(int left, int t o p , int width, int h e i g h t , b o o l e a n raised)


Этот метод выводит сплошной прямоугольник с контуром 3D.

p u b l i c v o i d drawArc(int left, int t o p , int width, int height, int startAngle,


int deltaAngle)
Метод d r a w A r c отображает фрагмент конт)ра овала. Первые четыре параметра
определяют прямоугольник, ограничивающий овал. Углы задают часть контура,
предназначенную для вывода; их значения отсчитываются против часовой стрел­
ки, а О означает восточное направление (3 часа). В отличие от тригонометриче­
ских функций в классе Math, углы задаются не в радианах, а в градусах.

p u b l i c v o i d fillArc(int left, int t o p , int w i d t h , int h e i g h t , int startAngle,


int deltaAngle)
Данный метод отображает закрашенный сегмент сектора (см. d r a w A r c ) .

public v o i d d r a w l m a g e ( l m a g e i m a g e , int left, int t o p , I m a g e O b s e r v e r observer)


Данный метод выводит изображение исходного размера. Для создания изображе­
ния используется метод g e t Image класса A p p l e t или T o o l k i t . Заметьте, что
g e t Image действует асинхронно, и если вы вызовете d r a w l m a g e сразу же после
вызова g e t Image, то, возможно, получите пустое изображение. Объект)^
I m a g e O b s e r v e r передается аплет или окно (переменная t h i s ) . Пс:)дробно работа
с изображениями будет рассмотрена далее в этой главе.

public void d r a w l m a g e ( l m a g e i m a g e , int left, int t o p , int width, int h e i g h t ,


I m a g e O b s e r v e r observer)
Данный метод выводит изображение, масштабированное в соответствии с задан­
ным прямоугольником ( l e f t , t o p , w i d t h , h e i g h t ) .

public void d r a w l m a g e ( l m a g e i m a g e , int left, int t o p . Color b g C o l o r ,


I m a g e O b s e r v e r observer) public v o i d d r a w l m a g e ( l m a g e i m a g e , int left, int t o p ,
int width, int h e i g h t . Color bgColor, I m a g e O b s e r v e r observer)
Эти методы представляют собой варианты двух рассмотренных выше методов и
предназначены для вывода прозрачных изображений. Для отображения прозрач­
ных пикселей используется заданный цвет фона.
338 Глава 9. Аплеты и основные действия с графикой

public void drawLine(int x l , int y l , int x 2 , int у2)


Метод d r a w L i n e выводит л и н и ю толщиной в 1 пиксель.

public void drawOval(int left, int t o p , int width, int height)


Этот метод выводит контур овала. Параметры задают координаты ограничиваю­
щего прямоугольника. Например, в результате вызова d r a w O v a l ( 7 5 , 7 5 , 5 0 , 50)
отобразится окружность радиусом 50 с центром в точке (100, 100).

public void fillOval(int left, int t o p , int width, int height)


Метод f i l l O v a l выводит закрашенный овал, ограниченный указанным прямо­
угольником.

public void drawPolygon(int[ ] xArray, int[ ] уАггау, int n u m P o i n t s )


public void d r a w P o l y g o n ( P o l y g o n p o l y g o n )
Эти методы отображают контуры многоугольника, заданного либо с помощью
массивов, либо посредством объекта P o l y g o n (класс, в котором хранится набор
точек). П о умолчанию многоугольник не замыкается. Чтобы вывести замкнутый
многоугольник, координаты первой и последней точек должны совпадать.

public v o i d fillPolygon(int[ ] хАггау, int[ ] уАггау, int n u m P o i n t s )


public void f i l l P o l y g o n ( P o l y g o n polygon)
С помощью данных методов отображаются закрашенные многоугольники. П о
)тлолчанию многоугольник замыкается; между первой и последней точками авто­
матически строится дополнительная линия.

p u b l i c void drawRect(int left, int t o p , int width, int height)


Этот метод выводит те1сущим цветом контур прямоугольника (толщиной в 1 пик­
сель). См. также drawBDRect и d r a w R o u n d R e c t .

public void fillRect(int left, int t o p , int w i d t h , int height)


Данный метод отображает прямоугольник, заполненный тек\тцим цветом. В на­
стоящее время AWT не поддерживает заполнение по шаблону или фрагментами
изображения, поэтому подобное заполнение необходимо реализовывать вручную.
См. также f i l l 3 D R e c t и f i l l R o u n d R e c t .

public void d r a w R o u n d R e c t ( i n t left, int t o p , int width, int h e i g h t , int arcWidth,


int arcHeight)
Этот метод выводит контуры прямоугольника со скругленными углами. Парамет­
ры a r c W i d t h и a r c H e i g h t .задают части кривой (в градусах) в верхней/нижнеР! и
л е в о й / п р а в о й частях. Если какой-либо из этих параметров равен нулю, отобра­
жаются прямые углы.

public void drawString(String string, int left, int bottom)


Д а н т л й метод выводит текущим шрифтом и цветом строку текста, левый нижний
угол которой находится в указанной позиции. Это один из немногих методов, в ко-
9 . 1 1 . Графические операции 339

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


массивов c h a r или b y t e применяются методы d r a w C h a r s и d r a w B y t e s .

Работа с цветом и со шрифтом


p u b l i c Color getColorO
Этот метод возвращает объект C o l o r , описывающий текущий цвет. Дополнитель­
ная информация об использовании заранее определенных и настраиваемых цве­
тов была приведена выше в этой главе при описании методов g e t B a c k g r o u n d и
setBackground.

public v o i d setColor(Color color)


Данный метод задает текущий цвет переднего плана. После создания объекта
G r a p h i c s рисование по умолчанию выполняется цветом переднего плана,
установленным для окна. Изменение цвета с помощью метода s e t C o l o r объекта
G r a p h i c s не изменяет настройки по умолчанию. П р и следующем вызове p a i n t
или g e t G r a p h i c s создается новый объект G r a p h i c s , в котором воссоздаются ус­
тановки по умолчанию. Для долговременных изменений служит метод s e t -
F o r e g r o u n d аплета, но заданный посредством этого метода цвет будет отобра­
жаться лишь при рисовании с помощью объекта G r a p h i c s , созданного после вы­
зова s e t F o r e g r o u n d .

public Font getFontO


В результате выполнения этого метода возвращается объект F o n t , описывающий
текущий шрифт. Ш р и ф т ы обсуждались ранее в этой главе при рассмотрении ме­
тодов g e t F o n t и s e t F o n t . Как класс Component (а следовательно, и дочерний
класс A p p l e t ) , так и класс G r a p h i c s включают метод g e t F o n t M e t r i c s , которому
в качестве параметра передается объект F o n t . Полученный в результате выполне­
ния данного метода объект F o n t M e t r i c s может использоваться для определения
размеров символов ( c h a r W i d t h ) или строк ( s t r i n g W i d t h ) , выведенных этим
шрифтом.

public void s e t F o n t ( F o n t font)


Данный метод устанавливает ш р и ф т для использования методом d r a w S t r i n g .
Изменения, выполненные посредством s e t F o n t , не сохраняются при следующем
вызове метода p a i n t или g e t G r a p h i c s . Для долговременного изменения шрифта
надо вызвать метод s e t F o n t аплета или другого соответствующего компонента.

Режимы рисования
p u b l i c void s e t X O R M o d e ( C o l o r color)
После вызова данного метода последующие операции рисования будут выполняться
в режиме XOR: цвет пикселя определяется результатом выполнения побитовой опе­
рации XOR между указанным цветом и текущим цветом пикселя в конкретной пози­
ции. Части линии, выведенной в режиме XOR, на многоцветном фоне будут отобра-
340 Глава 9. Аплеты и основные действия с графикой

жаться разными цветами. Цвет пикселя при рисовании в режиме XOR предсказать
трудно, поскольку операция исключающего XOR выполняется над внутренним
представлением цвета пикселей, которое может быть разным на различных маши­
нах. Если два раза подряд вывести одно и то же изображение в режиме XOR, инфор­
мация на экране вернется в исходном состояние. Этот эффект можно использовать
для стирания фрагментов, выведенных на фоне сложного изображения.
В данном режиме не следует использовать цвет C o l o r . b l a c k , поскольку на мно­
гих платформах он представляется нулевым значением и результаты операции
XOR будут совпадать с первоначальным цветом пикселей. Ч т о б ы вернуться в нор­
мальный режим рисования, надо вызвать метод s e t P a i n t M o d e .

public void setPaintMode()


Данный метод отменяет режим XOR и возвращает нормальный режим рисования,
т. е. рисование цветом переднего плана.

Координаты и области отсечения


public void clipRect(int left, int t o p , int width, int height)
Данный метод сокращает область отсечения и устанавливает ее равной пересече­
нию текущей области отсечения и заданного прямоугольника.

public R e c t a n g l e g e t C l i p B o u n d s ( )
Этот метод возвращает прямоугольник, определяющий текущую область отсече­
ния. В некоторых случаях может быть возвращено значение n u l l .

public Shape getClipO


Метод g e t C l i p возвращает объект S h a p e , описывающий область отсечения.

p u b i c void setClip(Shape c l i p p i n g R e g i o n )
Данный метод устанавливает новую область отсечения.

public v o i d translate(int deltaX, int deltaY)


Метод t r a n s l a t e перемещает начало координат в указанную точку.

9.12. Вывод изображений


Аплеты и приложения, написанные на Java, могут загружать и отображать стати­
ческие изображения в форматах GIF и JPEG, а также содержимое анимационных GIF-
файлов в формате GIF89A.
Вывод изображения осуществляется в два этапа. Сначала удаленное или локальное
изображение регистрируется с помощью метода g e t Image класса A p p l e t или
T o o l k i t . Затем изображение выводится на экран посредством метода d r a w Image
объекта G r a p h i c s . Вы можете вывести изображение с сохранением исходных разме­
ров либо явно задать его ширину и высоту. Следует помнить, что вызов метода
g e t Image не влечет за собой реальной загрузки изображения. В Java изображение не
загружается до тех пор, пока в этом не возникнет реальная необходимость.
9 . 1 2 . Вывод и з о б р а ж е н и й 341

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


ме; и может выводиться в процессе загрузки. Чтобы не ожидать того момента, когда
можно будет приступать к выводу- изображения, следует загрузить изображение зара­
нее, используя для этого метод p r e p a r e l m a g e либо объект M e d i a T r a c k e r . В первом
случае (использование p r e p a r e l m a g e ) изображение загружается в фоновом режиме,
а управление немедленно возвращается вызывающему методу. В этом случае работа
программы будет продолжаться, не дожидаясь завершения копирования через сете­
вое соединение (возможно, с низкой пропускной способностью). Если метод d r a w -
Image будет вызван до завершения загрузки изображения, будет выведена лишь его
часть, без какого-либо сообщения об ошибке. Часто метод d r a w l m a g e пытаются вы­
звать тогда, когда копирование еще не началось, и получают в результате пустое изо­
бражение. Метод p a i n t (или метод p a i n t C o m p o n e n t в случае "легковесных" Swing-
компонентов) вызывается после того, как подготовка изображения заканчивается,
поэтому, если d r a w l m a g e вызывается из тела метода p a i n t , изображение воспроиз­
водится полностью. Осуществляя вывод до этого момента, вы получите только часть
изображения, причем его ширина и высота могут быть некорректными. Если перед
выводом изображения вы хотите убедиться, что оно полностью готово, можете при­
менить второй подход, т.е. использовать класс M e d i a T r a c k e r .

Загрузка аплетом изображений^ заданных


с помощью относительного URL
Класс A p p l e t содержит метод g e t Image, которому при вызове передаются два
параметра: URL каталога и строка, определяющая имя файла относительно этого ка­
талога. В случае относительного URL в качестве параметра, определяющего его,
можно использовать вызов g e t C o d e B a s e (этот метод возвращает каталог, в котором
находится аплет) или g e t D o c u m e n t B a s e (этот метод возвращает каталог, в котором
расположена Web-страница). Для того, чтобы метод g e t Image завершился успешно,
должен быть установлен контекст аплета. Это означает, что первый вызов g e t Image
необходимо помещать в тело метода i n i t . Использование этого метода при иниц^га-
лизации переменной экземпляра Image недопустимо. Так, например, приведенное
ниже выражение следует считать некорректным.
p r i v a t e Image myImage = g e t l m a g e ( . . . ) ; // Ошибка

Внимание!

Попытка инициализировать nepei^eнtiyю экземпляра image приве­


дет к ошибке. Задавать ее значение надо в теле метода ±п±ьлибо в
методах, вызываемых после завершения ±n±t.

Для того чтобы вывести изображение, сл'^т^ует вызвать метод d r a w l m a g e класса


G r a p h i c s . Если вы выполняете рисование за пределами метода p a i n t (этому методу
автоматически передается контекст G r a p h i c s ) , то должны получить графический
контекст окна с помощью метода g e t G r a p h i c s . Ниже показаны два варианта вызова
drawlmage.
342 Глава 9. Аплеты и основные действия с графикой

drawlmage(image, left, top, window)


и
drawlmage(image, left, top, width, height, window)
В первом случае изображение выводится с сохранением исходных размеров. Во вто­
ром случае изображение масштабируется в соответствии с заданной прямо)тольной об­
ластью. Последний параметр представляет собой объект I m a g e O b s e r v e r ; в простых
случаях его роль выполняет текущее окно (аплет). Таким образом, если вывод изобра­
жения выполняется в теле метода p a i n t , в качестве последнего параметра метода
d r a w l m a g e почти всегда указывают переменную t h i s . В листрп1гах 9.11 и 9.12 показан
аплет, который загружает изображения из каталога i m a g e s , расположенного в катало­
ге, в котором находится аплет. Результаты выполнения аплета показаны на рис. 9.5.

Листинг 9 . 1 1 . JavaManl. j a v a

import Java.applet.Applet;
import java.awt.*;
/ * * Аплет, загружающий и з о б р а ж е н и е , у к а з а н н о е посредством
* о т н о с и т е л ь н о г о URL.
V
p u b l i c c l a s s JavaManl e x t e n d s A p p l e t {
p r i v a t e Image javaMan;

public void i n i t O {
javaMan = g e t I m a g e ( g e t C o d e B a s e ( ) , " i m a g e s / J a v a - M a n . g i f " )
}

public void paint(Graphics g) {


g.drawlmage(j avaMan, 0, 0, thi s);
}

Листинг 9.12. JavaManl. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>JavaManl</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>JavaManl</Hl>

<APPLET CODE="JavaManl.class" WIDTH=370 HEIGHT=365>


<B>Sorry, you have a Java-challenged browser.</B>
</APPLET>

</BODY>
</HTML>
9 . 1 2 . Вывод и з о б р а ж е н и й 343

НМ'Ги11|Д|1-|1Н1'И

' ^ -/ 3 :^1 ^ iSi а rf в ш т


JavaManl

Рис. 9.5. Как правило, в аплетах изображе­


ния загружаются с помощью вызова
getlmage(getCodeBase(), path) или
;fflg''=^ ^-^ :5! ъ^,:.: getlmage(getDocumentBase(), path)

Загрузка аплетом изображений, заданных


с помощью абсолютного URL
Использование абсолютных URL несколько сложнее по сравнению с относитель­
ными URL, так как в этом случае необходимо предусмотреть обработку исключения,
возникающего при недопустимом формате URL. (Обработка исключений рассматри­
валась в главе 8.) Более того, поскольку в большинстве броузеров объект S e c u r i t y -
Manager позволяет аплетам копировать изображения только с того же узла, на кото­
ром расположен сам аплет, загрузка изображений с помощью абсолютного URL
встречается достаточно редко. Однако вполне возможно, что каталог, в котором хра­
нятся изображения, не связан с каталогами, в котором находятся HTML-документы и
аплеты. В этом случае использование абсолютного URL вполне оправдано.
В листингах 9.13 и 9.14 приведен пример аплета и связанного с ним HTML-
документа. Результаты выполнения аплета показаны на рис. 9.6. Обратите внимание,
что конструктор URL помещен в блок t r y / c a t c h . Кроме того, заметьте, что аплет
импортирует пакет j a v a . n e t , содержащий классы URL и M a l f o r m e d U R L E x c e p t i o n . .
Использование абсолютного URL не отменяет того, что загрузка изображения откла­
дывается до тех пор, пока в нем не возникнет реальная необходимость. Если для ва­
шей программы такой подход неприменим, в последующих разделах будет показано,
как организовать предварительную загрузку изображения.
344 Глава 9. Аплеты и основные действия с графикой

Листинг 9.13. JavaMcm2. Java

import Java, applet .Applets-


import java.awt.*;
import java.net.*;

/** Аплет, который загружает изображение, заданное


* с помощью абсолютного URL. В составе URL указывается
* тот же компьютер, с которого был загружен аплет.
V
public class JavaMan2 extends Applet {
private Image javaMan;

public void initO {


try {
URL imageFile = new URL("http://www.corewebprogramming.com" +
"/images/Java-Man.gif") ;
javaMan = getlmage(imageFile);
} catch(MalformedURLException mue) {
showStatus("Bogus image URL.");
System.out.println("Bogus URL");
}
}

public void paint(Graphics g) {


g.drawlmage(javaMan, 0, 0, this);
}

Листинг 9.14. JavaMan2.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>JavaMan2</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>JavaMan2</Hl>

<APPLET C0DE="JavaMan2.class" WIDTH=370 HEIGHT=365>


<B>Sorry, you have a Java-challenged browser.</B>
</APPLET>

</BODY>
</HTML>
9 . 1 2 . Вывод и з о б р а ж е н и й 345

irft int^nei Exnfor^

• A j j d t e s s j ^ Ы1р/'/\^wwcofewebptogtarnrningcorn/JavaMan2htm| J ^ ^fi^So

JavaMan2

Рис. 9.6. Аплеты могут использовать


абсолютные URL для загрузки
jd изображений, но в этом случае действуют
|^Ap0!et«terted ^M}>D3«ipuJ« ограничения, связанные с безопасностью

Загрузка изображений приложениями


Графические приложения могут загружать изображения, заданные с помощью аб­
солютных URL, по сети или из локальных файлов. Для этого используется метод
g e t Image класса T o o l k i t . Относительные URL в данном случае неприменимы, по­
скольку приложения не связываются с Web-страницами. Несмотря на то что прило­
жения, в отличие от аплетов, не используют вариант g e t Image с двумя параметрами,
объект URL может быть создан на базе существующего URL и имени файла. Этот под­
ход используется в том случае, когда необходимо загружать несколько изображений,
хранящихся в одном каталоге. Текущий объект T o o l k i t можно получить из любого
графического объекта, вызывая g e t T o o l k i t либо T o o l k i t . g e t D e f a u l t T o o l k i t .
Например, в листинге 9.15 создается объект J P a n e l , который используется для выво­
да изображения. Имя файла создается относительно каталога, содержащего прило­
жение; при этом применяется вызов System, g e t P r o p e r t y ( " u s e r . d i r " ) . При этом
упрощается процесс переноса приложения в другой каталог либо на другой компью­
тер. Результаты выполнения приложения показаны на рис. 9.7. Исходный код класса
W i n d o w U t i l i t i e s , который инкапсулирует панель и присоединяет WindowLis-
t e n e r , будет рассмотрен в главе 14.

М е т о д и к а профессионалов

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


приложение, ссылайтесь на локальные файлы.
346 Глава 9. Аплеты и основные действия с графикой

Листинг 9.15. JavaManS.Java

import java.awt.*;
import javax.swing.*;

/** Приложение, которое загружает изображение из локального


* файла. Аплетам подобные действия запрещены.

class JavaMan3 extends JPanel {


private Image javaMan;

public JavaMan3 () {
String imageFile = System.getProperty("user.dir") +
"/images/Java-Man.gif";
javaMan = getToolkitO .getImage(imageFile);
setBackground(Color.white);

public void paintComponent(Graphics g) {


super.paintComponent(g);
g.drawlmage(javaMan, 0, 0, this);
}
public static void main(String[] args) {
JPanel panel = new JavaMan3();
WindowUtilities.setNativeLookAndFeel() ;
WindowUtilities.openlnJFrame(panel, 380, 390);
}

Рис. 9.7. Для загрузки изображений


приложением используется вызов
getToolkitO .getlmage (arg) или
Toolkit.getDefaultToolkit().getlmage(arg)
9.13. Предварительная загрузка изображений 347

9.13. Предварительная загрузка изображений


Часто бывает необходимо, чтобы система начала загрузку изображения как можно
скорее и не откладывала ее до того момента, когда вы попытаетесь отобразить его с
помощью метода d r a w I m a g e . Это справедливо для тех случаев, когда изображение
должно выводиться в ответ на такие действия пользователя, как щелчок на кнопке
или выбор пункта меню. В такой ситуации желательно, чтобы к моменту выполнения
действия изображение было полностью загружено. Метод p r e p a r e Image начинает
загрузку изображения в фоновом режиме и немедленно возвращает управление вы­
зывающему методу. Существуют два варианта p r e p a r e Image; по одному для каждого
из вариантов d r a w l m a g e .
preparelmage(image, window)
и
preparelmage(image, width, height, window)
Каждый раз, когда вы изменяете размеры изображения, это изображение рас­
сматривается как новое, поэтому убедитесь, что вы вызываете p r e p a r e l m a g e для ка­
ждого из планируемых выводов изображения в новом масштабе. В листинге 9.16 по­
казан код приложения, которое выводит изображение только в том случае, когда
пользователь щелкает на кнопке. Время, прошедшее с момента активизации кнопки
до завершения вывода изображения, отображается в поле редактирования. Если в
командной строке задается опция - p r e l o a d , вызывается метод p r e p a r e l m a g e . На
рис. 9.8 показаны результаты работы приложения, когда предварительная загрузка
изображения не используется. На рис. 9.9 показаны результаты работы с тем же изо­
бражением, когда задана опция p r e l o a d и между двумя последовательными щелчка­
ми на кнопке прошло несколько секунд. Конечно, значение времени, показанное на
рис. 9.8, может изменяться в зависимости от пропускной способности сетевого со­
единения, но из данного примера ясно, что если вы не используете p r e p a r e l m a g e ,
время между щелчками на кнопке расходуется напрасно.
В данный момент вам не обязательно детально разбираться в коде, представлен­
ном в листинге 9.16; вопросы организации пользовательского интерфейса будут рас­
смотрены в последующих главах. Сейчас вам важно сосредоточить внимание на мето­
де r e g i s t e r l m a g e , который вызывается из конструктора P r e l o a d .

Листинг 9 . 1 6 . P r e l o a d . J a v a

import java.awt.^;
import Java.awt.event.*;
import javax.swing.*;
import java.net.*;

/** К л а с с , в котором с р а в н и в а е т с я время вывода изображения


* при и с п о л ь з о в а н и и п р е д в а р и т е л ь н о й з а г р у з к и
"^ ( g e t l m a g e , p r e p a r e l m a g e и drawlmage) и б е з нее
* ( g e t l m a g e and d r a w l m a g e ) .
* <Р>
* Полученные результаты зависят от пропускной способности
* сетевого соединения и размера изображения, однако
348 Глава 9. Аплеты и основные действия с графикой

* становится очевидно, что время вывода предварительно


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

public class Preload extends JPanel implements ActionListener {

private JTextField timeField;


private long start = 0;
private boolean draw = falser-
private JButton buttons-
private Image platen-
public Preload(String imageFile, boolean preload) {
setLayout(new BorderLayout() K*
button = new JButton("Display Image"/,
button.setFont(new Font("SansSerif", Font.BOLD, 24));
button.addActionListener(this);
JPanel buttonPanel = new JPanel();
buttonPanel.add(button);
timeField = new JTextField(25);
timeField.setEditable(false);
timeField.setFont(new Font("SansSerif", Font.BOLD, 24));
buttonPanel.add(timeField);
add(buttonPanel, BorderLayout.SOUTH);
registerlmage(imageFile, preload);
}
/"** Выявлять объект, ответственный за возникновение
* события, нет необходимости, поскольку это может
* быть только кнопка.
^/

public void actionPerformed(ActionEvent event) {


draw = true;
start = System.currentTimeMillis0;
repaint();
}
// Вызов getImage и, при выполнении определенных
// условий, начало загрузки.

private void registerlmage(String imageFile, boolean preload) {


try {
plate = getToolkit().getlmage(new URL(imageFile));
if (preload) {
preparelmage(plate, this);
}
} catch(MalformedURLException mue) {
System.out.println("Bad URL: " + mue);
}
}

/** Если был щелчок на кнопке, выводится изображение


* и отображается время, потребовавшееся для этого, do nothing.
* В противном случае никакие действия не выполняются.
9.13. Предварительная загрузка изображений 349

*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (draw) {
g.drawlmage(plate, 0, 0, this);
showTime();
}
}

// Время задержки отображается в поле редактирования,


private void showTime() {
timeField.setText("Elapsed Time: " + elapsedTime() +
" seconds.");
}
// Время в секундах с момента щелчка на кнопке.

private double elapsedTime() {


double delta = (double)(System.currentTimeMillis() - start);
return(delta/1000.0);
}
public static void main(String[] args) {
JPanel preload;

if (args.length == 0) {
System.out.println("Must provide URL");
System.exit(0);
}
if (args.length == 2 && args[1].equals("-preload")) {
preload = new Preload(args[0], true);
} else {
preload = new Preload(args [0], false);
}
WindowUtilities.setNativeLookAndFeel();
WindowUtilities.openlnJFrame(preload, 1000, 750);
}
350 Глава 9. Аплеты и основные действия с графикой

Рис. 9.8. Результаты работы приложения, когда предварительная загрузка


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

Рис. 9.9. Результаты работы приложения при использовании метода p r e ­


pare image; в этом случае загрузка изображения начинается немедленно
9.14. Управление загрузкой изображений... 351

9.14. Управление загрузкой изображений:


ожидание изображения и проверка
состояния
Даже если вы предварительно загружаете изображения, иногда перед выполнением
конкретных действий необходимо убедиться в том, что загрузка изображения окончена.
Так, например, если программа должна отобразить рамку вокруг изображения, она не
может сделать это, поскольку до окончания загрузки значения высоты и ширины неиз­
вестны. Примером типичной ошибки разработчиков является код, приведенный в лис­
тингах 9.17 и 9.18. Высота и ширина изображения запоминаются в методе i n i t , и на
основе этих данных в методе p a i n t отображается прямоугольник. Результаты выпол­
нения кода в Internet Explorer 5, работающем в среде Windows 98, показаны на рис. 9.10.
Прямоугольник отсутствует, поскольку его высота определена как -1.

Листинг 9.17.ImageBox.Java

import Java.applet.Applet/
import java.awt.*;

/** К л а с с , который н е к о р р е к т н о з а г р у ж а е т изображение и


* п ы т а е т с я вывести рамку в о к р у г н е г о .
* Не п о в т о р я й т е подобных ошибок!
V

p u b l i c c l a s s ImageBox e x t e n d s A p p l e t {
p r i v a t e i n t imageWidth, imageHeight;
p r i v a t e Image i m a g e ;

public void i n i t O {
S t r i n g imageName = g e t P a r a m e t e r ( " I M A G E " ) ;
i f (imageName != n u l l ) {
image = g e t l m a g e ( g e t D o c u m e n t B a s e ( ) , imageName);
} else {
image = g e t l m a g e ( g e t D o c u m e n t B a s e ( ) , " e r r o r . g i f " ) ;
}
setBackground(Color.white);

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


/ / не загружено и методы в е р н у т з н а ч е н и е - 1 .
imageWidth = i m a g e . g e t W i d t h ( t h i s ) ;
imageHeight = i m a g e . g e t H e i g h t ( t h i s ) ;

p u b l i c v o i d p a i n t ( G r a p h i c s g) {
g . d r a w l m a g e ( i m a g e , 0, 0, t h i s ) ;
g . d r a w R e c t ( 0 , 0, i m a g e W i d t h , i m a g e H e i g h t ) ;
}
352 Глава 9. Аплеты и основные действия с графикой

Листинг 9 . 1 8 . I m a g e B o x , h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>ImageBox</TITLE>
</HEAD>
<BODY>
<Hl>ImageBox</Hl>
<APPLET C O D E = " I m a g e B o x . c l a s s " WIDTH=235 HEIGHT=135>
<PARAM NAME="IMAGE" V A L U E = " i m a g e s / s u r f i n g . g i f " >
S o r r y , you n e e d a < B > r e a l < / B > b r o w s e r .
</APPLET>
</BODY>
</HTML>

; fiie gtU Ут* Favorite» bxh


-> -
J
Ima geBox
1 .^-'


^ » Рис. 9.10. Определяя размеры изображения

« ] Done J^ MyCorapUa
Щ до окончания его загрузки, вы можете получить
неверные результаты

Решить данную проблему можно, используя класс M e d i a T r a c k e r . Этот класс по­


зволяет начать загрузку одного или нескольких изображений, зарегистрировать их с
помощью метода a d d Image и на некотором этапе выполнения программы дождаться
завершения загрузки изображений (методы w a i t F o r l D и w a i t F o r A l l ) . Класс
M e d i a T r a c k e r также содержит различные методы, позволяющие выявить ситуацию,
когда файл изображения не найден, и обработать другие ошибки. Краткое описание
методов приведено ниже.

MediaTracker
public void a d d l m a g e ( l m a g e i m a g e , int id)
public void a d d l m a g € ( l m a g e i m a g e , int id, int width, int height)
Данные методы регистрируют обычное или масштабированное изображение под
определенным идентификационным кодом. Зарегистрировав одно или несколько
изображений, вы можете проверять их состояние либо дожидаться изображения с
определенным идентификатором. Вы также можете ожидать загрузки всех изо­
бражений; в этом случае система старается загрузить первыми изображения с бо­
лее низкими значениями идентифик ционных номеров.
9.14. Управление загрузкой изображений... 353

public b o o l e a n checkAll()
public b o o l e a n c h e c k A l l ( b o o l e a n startLoading)
Данные методы возвращают значение t r u e , если загрузка всех изображений, за­
регистрированных с помощью M e d i a T r a c k e r , завершена. В противном случае
возвращается значение f a l s e . Если значение параметра s t a r t L o a d i n g равно
t r u e , система начнет загружать те изображения, загрузка которых еще не нача­
лась. Для ожидания загрузки изображений не следует помещать вызов C h e c k A l l в
цикл, лучше использовать метод w a i t F o r A l l , который позволяет добиться тех же
результатов, более экономно используя ресурсы центрального процессора.

public b o o l e a n c h e c k I D ( i n t id)
public b o o l e a n c h e c k I D ( i n t id, b o o l e a n startLoading)
Данные методы похожи на c h e c k F o r A l l , но с их помощью проверяется лишь со­
стояние изображения с конкретным идентификатором.

p u b l i c Object[ ] getErrorsAnyO
p u b l i c Object[ ] getErrorsID(int id)
Данные методы возвращают массив изображений, в процессе загрузки которых
возникла ошибка.

public b o o l e a n isErrorAnyO
public b o o l e a n isErrorID(int id)
Эти методы возвращают значение t r u e , если в процессе загрузки изображения
возникла ошибка. В противном случае возвращается значение f a l s e .

public void r e m o v e I m a g e ( I m a g e image)


public void r e m o v e I m a g e ( I m a g e i m a g e , int id)
public void r e m o v e I m a g e ( I m a g e i m a g e , int id, int width, int height)
Данные методы позволяют отменить регистрацию изображения.

public int statusAll()


public int statusID(int id, b o o l e a n startLoading)
Данные методы возвращают значение, полученное в результате выполнения опе­
рации поразрядного И Л И над флагами состояния загружаемых изображений.
В классе M e d i a T r a c k e r определены следующие флаги состояния: M e d i a T r a c ­
k e r . LOADING, MediaTracker.ABORTED, MediaTracker.ERRORED и M e d i a T r a c ­
k e r . COMPLETE. Состояние изображений, загрузка которых не началась, соответ­
ствует нулевому значению. Если значение параметра s t a r t L o a d i n g равно t r u e ,
система начнет загрузку тех изображений, для которых это еще не произошло.
354 Глава 9. Аплеты и основные действия с графикой

public void waitForAllO


public boolean waitForAll(long milliseconds)
Данные методы начинают загружать те изображения, загрузка которых еще не на­
чалась; управление вызывающему методу не возвращается до тех пор, пока все
изображения не будут загружены, либо до истечения указанного интервала време­
ни. Загрузка изображений с более низкими значениями идентификаторов начина­
ется в первую очередь. Данные методы могут генерировать исключение I n t e r -
r u p t e d E x c e p t i o n , поэтому при написании программы необходимо предусмот­
реть его обработку.

public void waitForID(int id)


public boolean waitForID(int id, long milliseconds)
Данные методы загружают изображение, которое зарегистрировано под указан­
ным идентификатором и загрузка которого еще не началась. Управление вызы­
вающему методу не возвращается до тех пор, пока изображение не будет загруже­
но, либо до истечения указанного интервала времени. Данные методы могут гене­
рировать исключение I n t e r r u p t e d E x c e p t i o n , поэтому при написании про­
граммы необходимо предусмотреть его обработку.
В листингах 9.19 и 9.20 показан вариант аплета ImageBox, который перед тем, как
определять размеры изображения, ожидает окончания его загрузки. Результат пока­
зан на рис. 9.11.

Листинг 9.19.BetterImageBox.java

import Java, a p p l e t .Applets-


import j a v a . a w t . * ;
/** Данный класс устраняет проблему, возникшую в ImageBox,
* путем использования MediaTracker. Перед
* определением размеров изображение оказывается загруженным.

p u b l i c c l a s s BetterlmageBox extends Applet {


p r i v a t e i n t imageWidth, imageHeight;
p r i v a t e Image images-
p u b l i c void i n i t O {
S t r i n g imageName = getParameter("IMAGE");
i f (imageName != n u l l ) {
image = getlmage(getDocumentBase(), imageName);
} else {
image = getlmage(getDocumentBase(), " e r r o r . g i f " ) ;
}
setBackground(Color.white);
MediaTracker t r a c k e r = new M e d i a T r a c k e r ( t h i s ) ;
tracker.addlmage(image, 0 ) ;
try {
tracker.waitForAll();
} catch(InterruptedException i e ) {}
9.14. Управление загрузкой изображений... 355

if (tracker.isErrorAnyО) {
System.out.println("Error while loading image");
)
// К этому моменту загрузка изображения завершена.
imageWidth = image.getWidth(this);
imageHeight = image.getHeight(this);
}

p u b l i c v o i d p a i n t ( G r a p h i c s g) {
g . d r a w l m a g e ( i m a g e , 0, 0, t h i s ) ;
g . d r a w R e c t ( 0 , 0, i m a g e W i d t h , i m a g e H e i g h t ) ;
}

Листинг 9 . 2 0 . B e t t e r I m a g e B o x . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>ImageBox</TITLE>
</HEAD>
<BODY>
<Hl>ImageBox</Hl>
<APPLET C O D E = " B e t t e r I m a g e B o x . c l a s s " WIDTH=235 HEIGHT=135>
<PARAM NAME="IMAGE" V A L U E = " i m a g e s / s u r f i n g . g i f " >
S o r r y , you n e e d a < B > r e a l < / B > b r o w s e r .
</APPLET>
</BODY>
</HTML>

1 № £« 1 ^ l^svoi» lock U « l " ^ 9 |

J
ImageBox

Ш^т Рис. 9.11. Используя MediaTracker, вы можете


,JP3 Pone ^ My СощяАел A дождаться окончания загрузки изображения

Поскольку M e d i a T r a c k e r чаще всего используется для ожидания загрузки изо­


бражений и проверки на наличие ошибок, решение этих двух задач удобно объеди­
нить в одном методе. В листинге 9.21 показан код класса T r a c k e r U t i l с двумя стати­
ческими методами: w a i t F o r l m a g e и w a i t F o r I m a g e s . Метод w a i t F o r l m a g e приме­
няется следующим образом:
356 Глава 9. Аплеты и основные действия с графикой

someImage = getImage(...);
doSomeOtherStuff();
if (TrackerUtil.waitForlmage(somelmage, this))
// Загрузка somelmage завершилась,
else
// Ошибка при загрузке somelmage.
Аналогично, код, в котором используется метод wait For Images, имеет вид
imagel = getlmage(...);
image2 = getlmage(...);

imageN = getlmage(...);
doSomeOtherStuff() ;
Image[] images = { imagel, image2, ... , imageN };
if (TrackerUtil.waitForlmages(images, this))
// Загрузка всех изображений завершилась,
else
/ / В о время загрузки изображения возникла ошибка.
Если вам требуется такая степень контроля над загрузкой изображения, которую
не может обеспечить класс M e d i a T r a c k e r , надо переопределить метод i m a g e U p d a t e
окна. Дополнительную и н ф о р м а ц и ю вы найдете в документации по API.

Листинг 9 . 2 1 . T r a c k e r U t i l , J a v a

import java.awt.*;
/** Вспомогательный класс, который позволяет загружать
* изображения и дожидаться окончания этой процедуры.
* Если вам необходимо последовательно загрузить несколько
* изображений, вызовите несколько раз waitForlmage. Если
* вам надо, чтобы изображения загружались одновременно,
* используйте метод waitForImages; при этом вы можете
* выиграть в скорости.
*/

public class TrackerUtil {


public static boolean waitForlmage(Image image. Component c) {
MediaTracker tracker = new MediaTracker(c);
tracker.addlmage(image, 0 ) ;
try {
tracker.waitForAll();
} catch(InterruptedException ie) {}
if (tracker.isErrorAny0) {
return(false) ;
} else {
return(true);
}
}

public static boolean waitForlmages(Image[] images.


Component c) {
MediaTracker tracker = new MediaTracker(c);
for(int i=0; i<images.length; i++) {
tracker.addlmage(images[i], 0 ) ;
9.15. Резюме 357

}
try {
tracker.waitForAll();
} catch(InterruptedException ie) {}
if (tracker.isErrorAny0) {
return(false);
} else {
return(true);
}

9.15. Резюме
Аплеты представляют собой графические программы, встраиваемые в Web-
страницы. Аплеты выполняются на клиентских машинах и на их работу накладывается
ряд ограничений, связанных с безопасностью системы. Аплет создается как подкласс
класса j a v a . a p p l e t .Applet и связывается с Web-страницей посредством элемента
APPLET. При создании аплета могут быть использованы новые графические компонен­
ты Swing, но большинство броузеров не поддерживает их. Для работы аплетов, приме­
няющих Swing, необходимо использовать Java Plug-In либо копировать классы Swing по
сети. Оба этих подхода неприменимы для клиентов, подключенных к Internet.
Графические Java-программы, не предназначенные для работы в Web, создаются
на основе класса JFrame. В аплетах рисование обычно осуществляется в теле метода
p a i n t . В приложениях операции рисования выполняются в методе p a i n t Component
"легковесного" компонента Swing; для этого к JFrame часто добавляется J P a n e l .
Обоим методам в качестве параметра передается объект G r a p h i c s , в котором реали­
зован набор методов для поддержки простых операций рисования. Если вам надо,
чтобы изображение на экране выглядело профессионально, необходимо использо­
вать средства Java 2D API, которые будут рассмотрены в следующей главе.
При работе с графическими изображениями приходится применять метод
drawlmage, который позволяет отображать изображения в формате GIF или JPEG,
загруженные ранее посредством метода g e t Image класса Applet или T o o l k i t . Изо­
бражения загружаются в фоновом потоке. Процесс загрузки можно непосредственно
контролировать с помощью MediaTracker. Для аплета изображения могут копиро­
ваться только с того компьютера, с которого был загружен сам аплет.
JAVA 2D:
ГРАФИКА
В JAVA 2

В этой главе...

• Вывод 20-форм.

• Способы заполнения форм.

• Использование локальных шрифтов.

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

• Управление прозрачностью объектов.

• Преобразование системы координат.


J~y\ZJSJ^

аждый, кто когда-либо имел дело с разработкой графических программ, при

К построении которых использовался Abstract Windowing Toolkit (AWT), навер­


ное, заметил, что возможности объекта G r a p h i c s весьма ограничены. Это не
удивительно, ведь компания Sun разрабатывала AWT за короткий период перехода
Java от встроенных приложений к World Wide Web. К недостаткам AWT относятся
ограниченный набор шрифтов, невозможность рисования линий толщиной более
одного пикселя, заполнение лишь одним цветом и отсутствие преобразований при
подготовке к выводу на принтер.
Java 2D — одно из самых мощных средств, реализованных на платформе Java 2. Его
превосходят только Swing-компоненты. Java 2D API представляет собой набор инст­
рументальных средств для разработки профессиональной высококачественной гра­
фики. В данной главе будут рассмотрены следующие возможности Java 2D.

• Цвета и шаблоны. П р и отображении графических элементов могут использо­


ваться градиенты цветов и заполнение по шаблону.
• П р о з р а ч н о е рисование. Прозрачность ф о р м ы определяется посредством спе­
циального параметра.
• Локальные ш р и ф т ы . Все локальные ш р и ф т ы , установленные на конкретной
платформе, могут применяться для отображения текста.
• Управление характеристиками пера. Разработчик может выбирать толщину
линий, шаблоны рисования линий и стили соединения сегментов.
• Преобразование системы координат — перенос, масштабирование и вращение.
Использование этих богатых возможностей усложняется тем, что Java 2D API входит
в состав пакета Java Foundation Classes, объявленного в Java 2. В отличие от Swing-
компонентов, которые могут быть добавлены к JDK 1.1, с Java 2D это сделать невозмож­
но. Для выполнения графических приложений 2D требуется среда выполнения Java 2, а
для работы аплетов— броузеры, поддерживающие Java 2 или оснащенные Java Plug-In,
360 Глава 10. Java 2D: графика в Java 2

которые мы рассмотрели в главе 9. Полная документация на Java 2D API и информация


для разработчиков находятся по адресу h t t p : / / j a v a . s u n . c o m / p r o d u c t s / j a v a -
media/2D/. JDK 1.3 включает демонстрационные программы 2D, размещенные в ката­
логе r o o t / j d k l . 3/demo/jfс/Java2D/. Java 2D также поддерживает высококачест­
венную печать; этот вопрос будет рассмотрен в главе 15.

1 0 . 1 . Общие сведения о Java 2D


В Java 2 методу paintComponent передается объект Graphics2D, который обес­
печивает гораздо большие графические возможности, чем объект AWT G r a p h i c s .
Однако для совместимости с Swing тип параметра метода paintComponent объявлен
как G r a p h i c s (Graphics2D является дочерним классом Graphics). Поэтому, перед
тем как выполнять рисование, необходимо преобразовать объект G r a p h i c s к типу
Graphics2D. Реально в Java 2 всем методам, работающим с объектом G r a p h i c s
( p a i n t , paintComponent, g e t G r a p h i c s ) , передается объект Graphics2D.
Общепринятый подход к выводу графической информации в Java 1.1 представлен
в листинге 10.1. Здесь в каждом AWT-объекте Component определен метод p a i n t .
Этому методу передается объект G r a p h i c s , с помощью которого и выполняется ри­
сование. В листинге 10.2 показан основной подход к отображению графики в Java 2D.
Для выполнения рисования все Swing-компоненты вызывают метод paintCompo­
n e n t . Объект Graphics2D можно использовать и в методе p a i n t AWT, но класс
G r a p h i c s 2D входит в состав только Java Foundations Classes, поэтому наилучшим ре­
шением в этом случае будет рисование посредством компонентов Swing, например
J P a n e l . Исключением является непосредственное 2D-pHCOBaHHe в методе p a i n t
объектов JFrame, J A p p l e t или JWindow, поскольку они являются "тяжеловесными"
Swing-компонентами и в них отсутствует метод paintComponent.

Листинг 10.1. Вывод графической информации в Java 1.1

public void paint(Graphics g) {


// Установка параметров пера.
g.setColor(someColor);
g.setFont(someLimitedFont);

// Отобрсикение формы.
g.drawstring(. . . ) ;
g.drawLine(...
g.drawRect(... // контур
g.fillRect(... // заполнение
g.drawPolygon( . . ) ; // контур
g.fillPolygon( . . ) ; // заполнение
g.drawOval(... // контур
g.fillOvaK. . . ] // заполнение
1 0 . 1 . О б щ и е с в е д е н и я о Java 2 D 361

Листинг 10.2. Вывод графической информации на платформе Java 2

public void paintComponent(Graphics g) {


// Очистка фона.
super.paintComponent(g);
// Приведение Graphics к 6raphics2D.
Graphics2D g2d = (Graphics2D)g;
// Установка паргкметров пера.
g2d.setPaint(fillColorOrPattern);
g2d.setStroke(penThicknessOrPattern) ;
g2d.setComposite(someAlphaComposite);
g2d.setFont(anyFont);
g2d.translate(...);
g2d.rotate(...);
g2d.scale(...);
g2d.shear(...);
g2d.setTransform(someAffineTransform);
// Создание формы.
SomeShape s = new SomeShape(...);
// Рисование формы.
g2d.draw(s); // контур
g2d.fill(s); // заполнение

Общие принципы рисования в Java 2D описаны ниже.


Преобразование объекта Graphics к типу Graphics2D
В теле paintComponent необходимо в первую очередь вызвать метод
paintComponent суперкласса, поскольку компоненты Swing по умолчанию вызы­
вают метод p a i n t связанного объекта ComponentUI; этим определяется внешний
вид компонента. Кроме того, по умолчанию метод paintComponent очищает кар­
ту пикселей, поскольку компоненты Swing реализуют двойную буферизацию. Да­
лее, ддя выполнения Java 2В-рисования объект G r a p h i c s необходимо преобразо­
вать к типу Graphics2 D.
public void paintComponent(Graphics g) {
super.paintComponent(g) ;
Graphics2D g2d = (6raphics2D)g;
g2d.doSomeStuff(...);

}
Методика профессионалов

Переопределяя метод ра±пЬСощ>опепЬ Swing-компонента, обяза­


тельно вызывайте super.paintComponent.

Модификация параметров рисования (необязательный этап)


Параметры рисования применяются не к объекту Shape, а к объекту Graphics 2D.
Изменения в графическом контексте (Graphics2D) находят отражение в после­
дующих операциях рисования Shape.
362 Глава 10. Java 2D: графика в Java 2

g2d.setPaint(fillColorOrPattern) ;
g2d.setStroke(penThicknessOrPattern);
g2d.setComposite(someAlphaComposite);
g2d.setFont(someFont);
g2d.translate(...);
g2d.rotate(...);
g2d.scale(...);
g2d.shear(...);
g2d.setTransform(someAffineTransform);

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


Rectangle2D.Double rect = ...;
Ellipse2D.Double ellipse = ...;
Polygon poly = ...;
GeneralPath path = ...;
// Соответствует интерфейсу Shape.
SomeShapeYouDefined shape = ...;

В ы в о д контуров или заполненного объекта Shape


Объект Shape надо передать методу draw либо методу fill объекта Graph ics2D.
Графический контекст, применяющийся к объекту Graphics2D, определяет, как
будет нарисована или заполнена форма.
g2d.draw (someShape) ;
g2d.fill(someShape);
Класс G r a p h i c s 2 D является подклассом класса G r a p h i c s , следовательно, он на­
следует все графические методы AWT, рассмотренные в главе 9. Кроме того, в классе
G r a p h i c s 2 D добавлены новые возможности по работе с графикой. После установки
графического контекста к отображению всех последующих объектов S h a p e применя­
ется тот же набор правил. Обратите внимание, что изменения, произведенные мето­
дами, воздействующими на систему координат (вращение, перемещение, масштаби­
рование), имеют тенденцию накапливаться.

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


Наиболее часто используемые методы класса G r a p h i c s 2 D описаны ниже.

public v o i d draw(Shape shape)


Данный метод выводит контур ф о р м ы , используя при этом текущие установки
контекста G r a p h i c s 2D. По умолчанию форма ограничена прямоугольником
( R e c t a n g l e ) , левый верхний угол которого расположен в позиции (0,0). Для того
чтобы разместить форму в другой позиции, необходимо применить преобразова­
ния к контексту G r a p h i c s 2 D : r o t a t e , t r a n s f o r m , t r a n s l a t e .
1 0 . 1 . Общие сведения о Java 2D 363

public b o o l e a n drawImage(BufferedImage i m a g e , B u f f e r e d l m a g e O p filter, int


left, int top)
Данный метод выводит объект Buf f e r e d l m a g e , верхний левый угол которого распо­
ложен в позиции ( l e f t , t o p ) . К изображению может быть применен фильтр ( f i l t e r ) .
Подробно использование Buf f e r e d l m a g e будет рассмотрено в разделе 10.3.

p u b l i c void drawString(String s, float left, float bottom)


Данный метод отображает строку символов, левый нижний угол которой располо­
жен в указанной позиции. Расположение строки задается с помощью значений с пла­
вающей точкой. Java 2D API не содержит вариант d r a w S t r i n g , поддерживающий
параметры типа d o u b l e , поэтому выражение d r a w S t r i n g ( s , 2 . 0 , 3 . 0 ) не будет
скомпилировано. Чтобы разрешить эту проблему, необходимо явно указать, что ли­
тералы принадлежат к типу f l o a t , например d r a w S t r i n g ( s , 2.Of, 3.0f).
Java 2D поддерживает дробные значения координат для того, чтобы обеспечить
более т о ч н о е масштабирование и преобразование системы координат. Объекты
Java 2D определяются в пользовательской системе координат (User Coordinate
Space), оси которой задаются с помощью значений с плавающей точкой. Перед
выводом на экран или на п р и н т е р пользовательская система координат преобра­
зуется в систему координат устройства (Device Coordinate Space). П р и преобразо­
вании 72 единицы пользовательской системы координат принимаются равными 1
дюйму на устройстве. Перед выводом дробные значения округляются до ближай­
ших целых.

p u b l i c v o i d fill(Shape shape)
Данный метод выводит заполненную форму в соответствии с текущими установ­
ками контекста G r a p h i c s 2D. Вопросы позиционирования рассмотрены в описа­
нии метода d r a w .

p u b l i c void rotate(double theta)


Метод r o t a t e выполняет вращение на t h e t a радиан вокруг точки с координата­
ми (0,0). Результаты r o t a t e добавляются к результатам операций вращения, вы­
полненным над контекстом G r a p h i c s 2 D ранее.

public v o i d rotate(double theta, d o u b l e x, d o u b l e y)


Метод r o t a t e выполняет вращение на t h e t a радиан вокруг точки с координата­
ми (х,у).

p u b l i c v o i d s c a l e ( d o u b l e x s c a l e , d o u b l e yscale)
Данный метод выполняет операции линейного масштабирования по осям х и у.
Значения, превышающие 1.0, растягивают оси, а значения менее 1.0— сжимают
их. Если для x s c a l e задано значение, равное -1, полученное изображение являет­
ся зеркальным отображением исходного относительно оси х. Если значение
y s c a l e равно -1, генерируется зеркальное отображение относительно оси у.
364 Глава 10. Java 2D: графика в Java 2

public void setComposite(Composite rule)


Данный метод определяет способ объединения пикселей новой формы с пикселя­
ми фона. Вы можете задать собственное правило объединения либо воспользо­
ваться одним из правил, заранее определенных в AlphaComposite:
A l p h a C o m p o s i t e . C l e a r , A l p h a C o m p o s i t e . D s t i n , AlphaComposite.DstOut,
AlphaComposite.DstOver, A l p h a C o m p o s i t e . S r c , AlphaComposite.Srcin,
AlphaComposite.SrcOut, AlphaComposite.ScrOver.
Чтобы создать новое правило AlphaComposite, надо вызвать метод g e t I n s t a n c e
следующим образом:
g2d.setComposite(AlphaComposite.SrcOver);

int type = AlphaComposite.SRC_OVER;


float alpha = 0.75f;
AlphaComposite rule =
AlphaComposite.getlnstance(type, alpha);
g2d.setComposite(rule);
Данный подход позволят задавать значение a l p h a , определяющее прозрачность
формы. По умолчанию значение прозрачности устанавливается равным 1.0
(непрозрачная форма). Правила объединения рассмотрены Т. Портером (Т.
Porter) и Т. Даффом (Т. Duff) в работе "Compositing Digital Images" SIGGRAPH 84,
p. 253-259.

public void setPaint(Paint paint)


Данный метод устанавливает стиль рисования в контексте Graphics2D. Допусти­
мым считается любой стиль, реализующий интерфейс P a i n t . Стили, поддержи­
ваемые на платформе Java 2, включают сплошной цвет Color, G r a d i e n t P a i n t и
TexturePaint.

public void setRenderingHints(Map hints)


Метод s e t R e n d e r i n g H i n t s позволяет контролировать качество рисования. AWT
включает класс R e n d e r i n g H i n t s , который реализует интерфейс Map и в котором
определен набор констант. Разработчик может управлять сглаживанием форм и
текста, передачей полутонов, интерполяцией между точками при преобразовани­
ях и частичным позиционированием текста. Как правило, сглаживание разреше­
но, а способ воспроизведения изображений ориентирован на обеспечение качест­
ва, а не скорости.
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RengeringHints.VALUE_ANTIALIAS_ON);
hints.add(new RenderingHints(
RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY));
public void setStroke(Stroke pen)
Способ рисования контуров определяется в контексте G r a p h i c s 2D на основе те­
кущего объекта S t r o k e . Метод s e t S t r o k e устанавливает объект S t r o k e , который
10.2. Отображение форм 365

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


интерфейс S t r o k e . В состав AWT входит класс B a s i c S t r o k e , задающий стили
рисования и объединения отрезков линий, а также шаблоны.

public void transform(AffineTransform matrix)


Данный метод задает матрицу аффинных преобразований, применяемую к контек­
сту Graphics2D. Эти преобразования включают сдвиг и вращение (см. раздел 10.7).

public void translate(double х, double у)


Метод t r a n s l a t e сдвигает начало координат на указанное число единиц. Сдвиг
добавляется к сдвигам, выполненным ранее в контексте Graphics2D. Единица от­
счета по координатным осям устанавливается равной 1/72 дюйма, что соответст­
вует одному пикселю на мониторе. Однако на принтерах с разрешением 300 или
600 dpi одна единица может отображаться в 4 или 9 пикселей.

public void setPaintModeO


Данный метод является переопределением метода setPaintMode класса Graphics.
Реализация этого метода также возвращает нормальный режим рисования (из ре­
жима XOR). Применительно к объекту Graphics2D вызов метода setPaintMode эк­
вивалентен выражению setComposite (AlphaComposite.SrcOver), посредством
которого форма отображается "поверх" фона.

public void setXORMode(Color color)


Данный метод переопределяет метод setXORMode класса Graphics. Для объекта
Graphics2D setXORMode определяет новое правило объединения в дополнение к
восьми правилам, рассмотренным ранее. В правиле объединения XOR не преду­
смотрено значение прозрачности. Результирующий цвет равен результату побито­
вой операции XOR между источником приемником и цветом, передаваемым опе­
рации XOR. Если выполнить рисование в режиме XOR дважды подряд, форма
вернется к исходному цвету. Значение прозрачности игнорируется, поэтому фор­
ма всегда непрозрачна. Сглаживание углов в данном режиме не поддерживается.

10.2. Отображение форм


В AWT для рисования формы вызываются методы drawXxx или f illXxx объекта
Graphics. В Java 2D сначала создается объект Shape, который затем передается в ка­
честве параметра методу draw или f i l l объекта Graphics2D. Например:
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// Считается, что к, у и diameter являются
// переменными экземпляра.
Ellipse2D.Double circle ss
new Ellipse2D.double(x, y, diameter, diameter);
g2d.fill(circle);
366 Глава 10. Java 2D: графика в Java 2

Для большинства классов Shape определены версии Shape. Double и


Shape . F l o a t . В зависимости от варианта класса координаты задаются либо числами
двойной точности (Shape. Double), либо обычными числами с плавающей точкой
(Shape. F l o a t ) . Сделано это потому, что на некоторых платформах числа с плаваю­
щей точкой обрабатываются быстрее, чем числа двойной точности. Поскольку
Graphics2D является дочерним классом класса G r a p h i c s , вы можете также исполь­
зовать для рисования знакомые вам методы drawXxx

Классы Shape
Параметры, передаваемые методам draw и f i l l класса Graphics2D, должны реа-
лизовывать интерфейс Shape. Вы можете создавать собственные формы либо ис­
пользовать встроенные классы, например Arc2D, Area, CubicCurve2D, E l l i p s e 2 D ,
G e n e r a l P a t h , Line2D, QuadCurve2D, Rectangle2D и RoundRectangle2D. Эти клас­
сы содержатся в пакете J a v a , awt. geom. Каждый из этих классов, за исключением
Area, Polygon и R e c t a n g l e , содержит конструкторы f l o a t и double.
Классы Polygon и R e c t a n g l e , которые поддерживались еще в Java 1.1, также реа­
лизуют интерфейс Shape. Эти формы были рассмотрены в главе 9.
Наиболее часто используемые конструкторы объектов Shape приведены ниже.

public Arc2D.Float(float left, float top, float width, float height,


float startAngle, float deltaAngle, int closure)
public Arc2D.Double(double left, double top, double width,
double height, double startAngle, double deltaAngle,
int closure)
Данные конструкторы строят дугу, выбирая часть эллипса, ограниченного прямо­
угольником, левый верхний угол которого лежит в точке ( l e f t , top). Центр дуги
(эллипса) лежит в центре ограничивающего прямоугольника. Углы отсчитывают-
ся от положительного направления оси х и выражаются в дуговых градусах. Дуго­
вые градусы отличаются от обычных геометрических градусов, в частности линия,
проведенная из центра дуги под углом 45 градусов, проходит через верхний пра­
вый угол ограничивающего прямоугольника. Значением параметра c l o s u r e мо­
жет быть Arc2D. CHORD, Arc2D. OPEN или Arc2D. PIE.
public Area(Shape shape)
Этот конструктор создает область (объект Area) по заданному объекту Shape.
Данные объекты поддерживают геометрические операции, такие как add,
s u b t r a c t , i n t e r s e c t и exclusiveOr.
public CubicCurve2D.Float(float xStart, float yStart,
float pX, float pY,
float qX, float qY,
float xEnd, float yEnd)
public CubicCurve2D.Double(double xStart, double yStart,
double pX, double pY,
double qX, double qY,
double xEnd, double yEnd)
10.2. Отображение форм 367

Эти конструкторы создают форму C u b i c C u r v e 2 D , представляющую кривую


(сплайн), расположенную между точками ( x S t a r t , y S t a r t ) и (xEnd, yEnd). Кри­
визна л и н и и управляется двумя контрольными точками (рХ, pY) и (qX, qY).

p u b l i c Ellipse2D.Float(float left, float t o p , float width, float height)


p u b l i c E l l i p s e 2 D . D o u b l e ( d o u b l e left, d o u b l e t o p , d o u b l e width, d o u b l e height)
Данные конструкторы создают эллипс, ограниченный прямоугольником размерами
w i d t h и h e i g h t . Класс E l l i p s e 2 D является дочерним классом R e c t a n g u l a r S h a p e
и поддерживает методы, общие для R e c t a n g l e 2 D и R o u n d R e c t a n g l e 2 D .

p u b l i c GeneralPathO
Класс G e n e r a l P a t h интересен тем, что позволяет определять отрезки различных
линий для создания нового объекта S h a p e . Этот класс поддерживает такие методы
построения и объединения прямых и кривых Безье, как c l o s e P a t h , c u r v e T o ,
l i n e T o , moveTo и quadTo. Если вы попытаетесь добавить к G e n e r a l P a t h отрезок,
не вызвав перед этим метод moveTo, будет сгенерировано сообщение об ошибке
I l l e g a l P a t h S t a t e E x c e p t i o n . Пример создания G e n e r a l P a t h приведен ниже.
G e n e r a l P a t h p a t h = new G e n e r a l P a t h O ;
p a t h . m o v e T o ( 1 0 0 , 100) ;
p a t h . l i n e T o ( 3 0 0 , 205) ;
p a t h . q u a d T o ( 2 0 5 , 2 5 0 , 34 0 , 3 0 0 ) ;
p a t h . l i n e T o ( 3 4 0, 350) ;
path.closePath();

p u b l i c Line2D.Float(float xStart, float yStart, float x E n d , float yEnd)


p u b l i c L i n e 2 D . D o u b l e ( d o u b l e xStart, d o u b l e yStart, d o u b l e x E n d , d o u b l e yEnd)
Данные конструкторы создают форму Line2D, которая представляет отрезок пря­
мой, ограниченный точками ( x S t a r t , y S t a r t ) и (xEnd, yEnd).

p u b l i c Line2D.Float(Point p i . P o i n t p2)
p u b l i c L i n e 2 D . D o u b l e ( P o i n t p i . P o i n t p2)
Данные конструкторы создают форму L i n e 2 D , которая описывает отрезок пря­
мой, ограниченный точками р 1 и р2 (точки представлены объектами P o i n t ) .

public QuadCur-ve2D.Float(float xStart, float yStart,


float pX, float pY,
float x E n d , float yEnd)
public Q u a d C u r v e 2 D . D o u b l e ( d o u b l e xStart, d o u b l e yStart,
d o u b l e pX, d o u b l e pY,
d o u b l e x E n d , d o u b l e yEnd)
Этот конструктор создает объект S h a p e , который представляет кривую, располо­
женную между точками ( x S t a r t , y S t a r t ) и (xEnd, yEnd). Точка (рХ, pY) управля­
ет кривизной линии.
368 Глава 10. Java 2D: графика в Java 2

public Rectangle2D.Float(float top, float left, float width, float height)


public Rectangle2D.Double(double top, double left, double width, double height)
Данные конструкторы создают форму Rectangle2D, т.е. прямоугольник шириной
width и высотой h e i g h t , верхний левый угол которого расположен в точке (top,
left).

public RoundRectangle2D.Float(noat top, float left,


float width, float height,
float arcX, float arcY)
public RoundRectangle2D.Double(double top, double left,
double width, double height,
double arcX, double arcY)
Эти два конструктора создают объект RectangleShape со скругленными углами.
Ширина и высота прямоугольника определяются параметрами width и h e i g h t , а
левый верхний угол расположен в точке (top, l e f t ) . Параметры а г сХ и a r c Y за­
дают расстояния от углов (по осям х и у), на которых прямые линии соединяются
со скругляющими кривыми.
пример отображения круга (объекта E l l i s p s e 2 D , ширина и высота которого
равны) и прямоугольника (Rectangle2D) приведен в листинге 10.3. Круг отобража­
ется полностью закрашенным, а для прямоугольника рисуется только его контур.
При рисовании используются установки контекста Graphics 2D, принятые по умол­
чанию. Результаты выполнения программы показаны на рис. 10.1. Метод g e t C i r c l e
будет применяться в других примерах, приведенных в данной главе. При работе
ShapeExample используется класс W i n d o w U t i l i t i e s (листинг 14.1) и класс
E x i t L i s t e n e r (листинг 14.2).
Примеры, приведенные в данной главе, оформлены как Java-приложения. Для то­
го чтобы преобразовать их в аплеты, можно использовать следующий шаблон:
import java.awt.*;
import javax.swing.*;
public class YourApplet extends JApplet {
public void initO {
JPanel panel « new ChapterExample();
panel.setBackground(Color.white);
getContentPane().add(panel);
}
}
При создании аплета надо создать объект JApplet и добавить код примера, реали­
зованный как JPanel, к c o n t e n t Рапе объекта JApplet. Для некоторых примеров при­
дется также установить цвет фона JPanel. После создания HTML-файла (размеры ап­
лета должны быть такими же, как и размеры исходного JFrame) вы можете использо­
вать a p p l e t viewer либо преобразовать HTML-файл для поддержки Java Plug-In. О
продукте Java Plug-In и конвертировании HTML-файла см. в разделе 9.9.
10.2. Отображение форм 369

Листинг 10.3.ShapeExample.Java

import javax.swing.*; // Для JPanel, и др.


import java.awt.*; // Для Graphics, и др.
import Java, awt .geom."" // Для Ellipse2D, и др.
/** Пример рисования/заполнения форм с помощью Java 2D
* в Java 1.2 и более поздних версиях.

public class ShapeExample extends JPanel {


private Ellipse2D.Double circle s=
new Ellipse2D.Double(10, 10, 350, 350);
private Rectangle2D. Dotible SG[uare «
new Rectangle2D.Double(10, 10, 350, 350);
public void paintComponent(Graphics g) {
clear (g);
Graphics2D g2d = (Graphics2D)g;
g2d.fill(circle);
g2d.draw(sG[uare) ;
}
// Поскольку no умолчанию применяется двойная буферизация,
. // super.paintComponent очищает внеэкранную карту пикселей,
protected void clear(Graphics g) {
super.paintComponent(g);
}
protected Ellipse2D.Double getCircleO {
return(circle);
}
public static void main(String[] args) {
WindowUtilities.openlnJFrame(new ShapeExample() 3 8 0 , 400)
}
}

Рис. 10.1. Эллипс (круг) и прямоугольник,


выведенные средствами Java 2D
370 Глава 10. Java 2D: графика в Java 2

10.3. Стили рисования


П р и заполнении ф о р м ы S h a p e объект G r a p h i c s 2 D использует установки, связан­
ные с внутренним атрибутом P a i n t . Для задания стилей используются классы C o l o r
(сплошной цвет), G r a d i e n t P a i n t (градиентное закрашивание, реализующее плав­
ный переход между двумя цветами) и T e x t u r e P a i n t (изображение в виде мозаики).
Кроме того, вы можете использовать собственный объект P a i n t . Для того чтобы за­
дать новые установки P a i n t и получить и н ф о р м а ц и ю о существующих, используются
соответственно методы s e t P a i n t и g e t P a i n t . Заметьте, что s e t P a i n t и g e t P a i n t
по своим возможностям превосходят методы s e t C o l o r и g e t C o l o r , используемые в
классе G r a p h i c s .

Классы рисования
Объект, передаваемый в качестве параметра методу s e t P a i n t класса G r a p h i c s 2 D , и
значение, возвращаемое методом g e t P a i n t , должны реализовывать интерфейс P a i n t .
Ниже описаны основные встроенные классы P a i n t .

Color
Класс C o l o r содержит те же константы для определения цвета, что и AWT-версия,
но предоставляет дополнительный конструктор, позволяющий задать значение про­
зрачности. Цвет представляется 4-байтовым целым числом, в котором три младших
байта соответствуют красному, зеленому и синему цветам, а старший байт задает значе­
ние прозрачности ( a l p h a ) . По умолчанию значение прозрачности равно 255, что соот­
ветствует непрозрачному цвету. Для полностью прозрачного цвета a l p h a равно 0. Кон­
структоры C o l o r имеют следующий вид:

public Color(int r e d , int g r e e n , int blue)


public Color(float int, float g r e e n , float blue)
Эти два конструктора по заданным компонентам r e d , g r e e n и b l u e создают непро­
зрачный объект C o l o r . Целочисленные значения ( i n t ) лежат в диапазоне от О до
255, а значения с плавающей точкой ( f l o a t ) — в диапазоне от O.Of до l.Of. П р и пре­
образовании значения f l o a t в значение i n t оно умножается на 255 и округляется.

public Color(int r e d , int g r e e n , int b l u e , int alpha)


public Color(float r e d , float g r e e n , float b l u e , float alpha)
Данные конструкторы создают объект C o l o r , для которого значение прозрачно­
сти задается с помощью параметра a l p h a . Допустимые значения параметров r e d ,
g r e e n , b l u e , и a l p h a были описаны выше.
Перед тем как приступить к рисованию, необходимо задать прозрачность ф о р м ы .
Для этого надо создать объект A l p h a C o m p o s i t e , а затем применить его к контексту
G r a p h i c s 2 D с помощью метода s e t C o m p o s i t e .
10.3. Стили рисования 371

GradientPaint
Объект G r a d i e n t P a i n t представляет плавный переход от одного цвета к другому.
Градиентная линия задается с помощью двух точек. Одной точке соответствует один
цвет, а другой точке — другой цвет. Цвет плавно изменяется вдоль градиентной ли­
нии, а линии, расположенные перпендикулярно ей, закрашиваются одним цветом.
В зависимости от значения логического параметра цветовой шаблон повторяется
вдоль продолжения градиентной линии до конца формы.

public GradientPaint(float xStart, float yStart,


Color colorStart, float x E n d , float yEnd,
Color colorEnd)
Этот конструктор создает объект G r a d i e n t P a i n t , начинающийся цветом
c o l o r S t a r t в точке ( x S t a r t , y S t a r t ) и заканчивающийся цветом c o l o r E n d в
точке (xEnd, yEnd). Цветовой шаблон не повторяется (один градиентный цикл).

public GradientPaint(float xStart, float yStart,


Color colorStart, float x E n d , float yEnd,
Color c o l o r E e n d , b o o l e a n repeat)
Данный конструктор действует как и предыдущий, за исключением того, что логи­
ческое значение r e p e a t управляет повторением цветов за пределами градиент­
ной линии.

TexturePaint
Класс T e x t u r e P a i n t представляет изображение, повторяющееся в пределах фор­
мы. Создавая объект T e x t u r e P a i n t , надо задать изображение и указать его размеры.

public T e x t u r e P a i n t ( B u f f e r e d I m a g e image, R e c t a n g l e 2 D tilesize)


Конструктор T e x t u r e P a i n t отображает Buf f e r e d l m a g e в R e c t a n g l e 2 D и распо­
лагает прямоугольники в виде мозаики. Для того чтобы создать Buf f e r e d l m a g e на
базе файла GIF или JPEG, необходимо выполнить ряд действий. Следует загрузить
Image обычным способом, получить размер изображения и создать
Buf f e r e d l m a g e , тип которого определяется как Buf f e r e d l m a g e .TYPE_INT_ARGB.
Затем необходимо получить объект G r a p h i c s Buf f e r e d l m a g e , вызвав для этого ме­
тод c r e a t e G r a p h i c s , и вывести Image в Buf f e r e d l m a g e с помощью d r a w l m a g e .

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


Градиентная линия начинается красным цветом ( C o l o r , r e d ) в точке (О, 0) и закан­
чивается желтым цветом ( C o l o r . y e l l o w ) в точке (185, 185) около центра круга. Гра­
диентные цвета повторяются в оставшейся части круга. Результат выполнения кода
показан на рис. 10.2.
372 Глава 10. Java 2D: графика в Java 2

Листинг 1 0 . 4 . GradientPaintEacample. j a v a

import java.awt.^;
/** Пример градиентного заполнения круга. Цвет изменяется
* от красного, в точке (0,0) до желтого — в точке (175,175).
V
public class GradientPaintExample extends ShapeExample {
private GradientPaint gradient =
new GradientPaint(0, 0, Color.red, 175, 175, Color.yellow,
true); // true означает повторение шаблона
public void paintComponent(Graphics g) {
clear (g);
Graphics2D g2d = (Graphics2D)g;
drawGradientCircle(g2d);
}
protected void drawGradientCircle(Graphics2D g2d) {
g2d.setPaint(gradient);
g2d.fill(getCircle());
g2d.setPaint(Color.black);
g2d.draw(getCircle0);
}

public static void main(String[] args) {


WindowUtilities.openlnJFrame(new GradientPaintExample(),
380, 400);
}
}

Рис. 10.2. Круг, нарисованный в Java 2D


с использованием градиентного заполнения
10.3. Стили рисования 373

Заполнение контура изображением


Для того чтобы заполнить контур изображениями, расположенными в виде мо­
заики ( t i l e d ) , необходимо создать объект T e x t u r e P a i n t и передать его методу
s e t P a i n t объекта G r a p h i c s 2 D так же, как и при закрашивании сплошным цветом
или градиентном заполнении. Конструктору T e x t u r e P a i n t в качестве параметров
передаются объекты Buf f e r e d l m a g e и R e c t a n g l e 2 D . Объект Buf f e r e d l m a g e опре­
деляет изображение, подлежащее выводу, а R e c t a n g l e 2 D указывает, с какой точки
следует размещать повторяющееся изображение. Прямоугольник также задает разме­
ры масштабированного изображения. Создание Buf f e r e d l m a g e для поддержки ри­
сования не составляет труда: достаточно вызвать конструктор Buf f e r e d l m a g e , ука­
зав ширину, высоту и тип Buf f e r e d l m a g e .TYPE_INT_RGB, а затем обратиться к ме­
тоду c r e a t e G r a p h i c s буферизованного изображения для получения объекта
G r a p h i c s 2 D . Например:
int width =32;
int height=32;
Bufferedlmage bufferedlmage =
new Bufferedlmage(width, height
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedlmage.createGraphics();
g2d.draw(someShape);

TexturePaint texture =
new TexturePaint(bufferedlmage,
new Rectangle(0, 0, width, height));

Объект G r a p h i c s 2 D , возвращенный методом c r e a t e G r a p h i c s , связан с


Buf f e r e d l m a g e . С этого момента рисование посредством G r a p h i c s 2 D приводит к
выводу в Buf f e r e d l m a g e . В данном примере "элементом мозаики" является квадрат­
ное изображение шириной и высотой 32 пикселя, в которое записывается буферизо­
ванное изображение.
Создать Buf f e r e d l m a g e на базе файла несколько сложнее. Во-первых, надо загру­
зить Image из файла и использовать M e d i a T r a c k e r , чтобы убедиться, что загрузка
окончилась. Затем необходимо создать пустой объект Buf f e r e d l m a g e , используя
ширину и высоту I m a g e . Далее следует получить объект G r a p h i c s 2D с помощью ме­
тода c r e a t e G r a p h i c s и вывести Image в Buf f e r e d l m a g e . Данная процедура реали­
зована в методе g e t B u f f e r e d l m a g e класса I m a g e U t i l i t i e s , код которого показан в
листинге 10.6.
П р и м е р создания изображения и использования его для заполнения представлен в
листинге 10.5. Результаты выполнения кода показаны на рис. 10.3. В этом примере
создаются два шаблона заполнения: один из них представляет собой изображение ка­
пли, а другой— ф о т о г р а ф и ю автора книги, обдумывающего очередную гениальную
идею. Первое из изображений применяется перед выводом треугольника, а второе —
перед рисованием прямоугольника. Размеры R e c t a n g l e и Buf f e r e d l m a g e совпада­
ют, поэтому изображение выводится лишь один раз.
374 Глава 10. Java 2D: графика в Java 2

Листинг 10.5. Tiledlmages.Java

import javax.swing.*;
import java.awt.*;
import Java.awt.geom.*;
import Java.awt.image.*;

/** Пример использования TexturePaint для заполнения объекта


* повторяющимися изображениями. Для получения изображения
* из файла и преобразования в Bufferedlmage используется
* метод getBufferedlmage класса ImageUtilities.
V
public class Tiledlmages extends JPanel {
private String dir = System.getProperty("user.dir");
private String imageFilel = dir + "/images/marty.jpg";
private TexturePaint imagePaintl;
private Rectangle imageRect;
private String imageFile2 = dir + "/images/bluedrop.gif";
private TexturePaint imagePaint2;
private int[] xPoints = { 30, 700, 400 };
private int[] yPoints = { 30, 30, 600 };
private Polygon imageTriangle = new Polygon(xPoints, yPoints, 3)
public Tiledlmages() {
Bufferedlmage image =
ImageUtilities.getBufferedlmage(imageFilel, this);
imageRect = new Rectangle(235, 70, image.getWidth(),

image.getHeight() ) ;
imagePaintl = new TexturePaint(image, imageRect);
image = ImageUtilities.getBufferedlmage(imageFile2, this);
imagePaint2 =
new TexturePaint(image, new Rectangle(0, 0, 32, 32));

public void paintComponent(Graphics g) {


super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setPaint(imagePaint2);
g2d.fill(imageTriangle);
g2d.setPaint(Color.blue);
g2d.setStroke(new BasicStroke(5) ) ;
g2d.draw(imageTriangle) ;
g2d.setPaint(imagePaintl);
g2d.fill(imageRect);
g2d.setPaint(Color.black);
g2d.draw(imageRect);
}
public static void main(String[] args) {
WindowUtilities.openlnJFrame(new Tiledlmages(), 750, 650);
10.3. Стили рисования 375

Рис. 10.3. Определяя объект TexturePaint,


можно организовать заполнение формы
повторяющимися изображениями

Листинг 10.6.ImageUtilities.Java

import java.awt.*;
import Java.awt.image.*;

/** Класс, упрощающий выполнение некоторых операций над


* изображениями, в частности создание Bufferedlmage
* на базе файла изображения и использование MediaTracker
* при ожидании окончания загрузки изображений.
V
public class ImageUtilities {

/** Создание Image на базе информации из файла и


* преобразование в Bufferedlmage.
V
piiblic static Bufferedlmage getBufferedlmage(String imageFile,
Component c) {
Image image = c.getToolkit().getImage(imageFile);
waitForImage(image, c ) ;

Bufferedlmage bufferedlmage =
new Bufferedlmage(image.getWidth(c), image.getHeight(c),
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedlmage.createGraphics();
g2d.drawImage(image, 0 , 0 , c ) ;
return(bufferedlmage);
}

/** Получение Image, связанного с файлом, и ожидание


* окончания загрузки. Если вы загружаете несколько
* изображений, не следует выполнять данную операцию
* многократно; лучше вызвать вариант метода,
* предназначенный для работы с массивом изображений.
376 Глава 10. Java 2D: графика в Java 2

public static boolean waitForlmage(Image image, Component c) {


MediaTracker tracker = new MediaTracker(c);
tracker.addlmage(image, 0 ) ;
try {
tracker.waitForAll();
} catch(InterruptedException ie) {}
return(!tracker.isErrorAny());
}
/** Получение нескольких изображений, связанных с файлом,
* и ожидание окончания загрузки.

public static boolean waitForlmages(Image[] images. Component c) {


MediaTracker tracker = new MediaTracker(c);
for(int i=0; i<images.length; i++)
tracker.addlmage(images[i], 0 ) ;
try {
tracker.waitForAll();
} catch(InterruptedException ie) {}
return(!tracker.isErrorAny());

10.4. Рисование прозрачных форм


Java 2D позволяет устанавливать при операциях рисования параметр ( a l p h a ) ,
определяющий прозрачность, так, что нижележащая картинка "просвечивает''
через нарисованную форму или изображение. Для установки прозрачности надо
создать объект A l p h a C o m p o s i t e , а затем передать его методу s e t C o m p o s i t e объекта
G r a p h i c s 2D. Объект AlphaComposite создается путем вызова метода
A l p h a C o m p o s i t e . g e t l n s t a n c e с указанием правил объединения и значения про­
зрачности, например:
f l o a t alpha = 0.75f;
i n t t y p e = AlphaComposite.SRC_OVER;
AlphaComposite composite =
AlphaComposite.getlnstance(type, alpha);
В A l p h a C o m p o s i t e API определено восемь встроенных правил объединения, но
для "прозрачного" рисования обычно применяется A l p h a C o m p o s i t e . SRC_OVER. Со­
гласно этому правилу, выводимое изображение помещается поверх существующего
изображения или фона. Значение a l p h a лежит в диапазоне от O.Of (полностью про­
зрачное) до 1.Of (непрозрачное).
Листинг 10.7 демонстрирует изменение параметра прозрачности, выполняемое
перед рисованием красного квадрата, который частично перекрывает синий квадрат.
На рис. 10.4 показаны 11 непрозрачных синих квадратов, расположенных в различ­
ных позициях в окне. Их перекрывают красные квадраты, при выводе которых были
заданы различные значения прозрачности. Для самого левого квадрата параметр
10.4. Рисование прозрачных форм 377

a l p h a равен O.Of; по мере движения вправо его значение возрастает и для самого пра­
вого квадрата оно равно 1 .Of.
Как говорилось в разделе 10.3, значение прозрачности может задаваться при соз­
дании цвета. В данном примере прозрачность красного квадрата указывается как па­
раметр конструктора класса Color.
p r i v a t e void drawSquares(Graphics2D g2d, f l o a t alpha) {
g2d.setPaint(Color.blue);
g2d.fill(blueSquare) ;
Color c o l o r = new Color(1, 0, 0, alpha); //Красный
g2d.setPaint(color);
g2d.fill(redSquare) ;
}

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


SRCOVER. Это правило используется объектом Graphics2D по умолчанию. Если па­
раметр a l p h a установлен как для объекта AlphaComposite, так и для объекта Color,
окончательное значение прозрачности вычисляется как произведение значений
alpha.

Листинг 10.7. TransparencyExample.Java

import javax.swing.*;
import java.awt.*;
import Java.awt.geom.*;

/** Пример использования AlphaComposite для создания


* частично прозрачных изображений.

public class TransparencyExample extends JPanel {


private static int gap=10, width=60, offset=20,
deltaX=gap+width+offset ;
private Rectangle
blueSquare = new Rectangle(gap+offset, gap+offset, width,
width),
redSquare = new Rectangle(gap, gap, width, width);

private AlphaComposite makeComposite(float alpha) {


int type = AlphaComposite. SRC_OVER;
return(AlphaComposite.getlnstance(type, alpha));
}
private void drawSquares(Graphics2D g2d, float alpha) {
Composite originalComposite = g2d.getComposite();
g2d.setPaint(Color.blue);
g2d.fill(blueSquare);
g2d.setComposite(makeComposite(alpha));
g2d.setPaint(Color.red);
g2d.fill(redSquare);
g2d.setComposite(originalComposite);
}
public void paintComponent(Graphics g) {
378 Глава 10. Java 2D: графика в Java 2

super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
for(int i=0; i<ll; i++) {
drawSquares(g2d, i*0.IF);
g2d.translate(deltaX, 0 ) ;
}

public static void main(String[] args) {


String title = "Transparency example: alpha of the top " н
"(red) square ranges from 0.0 at the left '
"to 1.0 at the right. Bottom (blue) square
"is opaque.";
WindowUtilities.openlnJFrame(new TransparencyExample(),
ll*deltaX + 2*gap,
deltaX + 3*gap,
title, Color.lightGray);

^hw.'^l!li^•rjlm•{^lM[rj^fiMl^^lii,тl[^•^jlf^irl'^^^^^l^^^l^^l/ilf1^,тlHl^я^

Рис. 10.4. Изменение прозрачности, в результате чего изображение,


расположенное на заднем плане, "просвечивает" через изображение
переднего плана

10.5. Использование локальных шрифтов


В Java 2D вы можете использовать те же ш р и ф т ы , что и в Java 1.1, а именно: Serif
(например. Times), SansSerif (например, Helvetica или Arial), Monospaced (например,
Courier), Dialog, и Dialoglnput. Кроме того, вы также можете применять локальные
ш р и ф т ы , установленные на вашем компьютере. Поиск в полном списке ш р и ф т о в мо­
жет занять несколько секунд. Для поиска ш р и ф т о в применяются методы
g e t A v a i l a b l e F o n t F a m i l y N a m e s и g e t A l l F o n t s объекта G r a p h i c s E n v i r o n m e n t .
Для получения G r a p h i c s E n v i r o n m e n t надо использовать следующее выражение:
GraphicsEnvironment env =

GrapicsEnvironment.getLocalGraphicsEnvironment();
Затем вызывается метод
env.getAvailableFontFamilyNames ();
или
env.getAllFontsо; / / Намного м е д л е н н е е !
Разделы документации по API, касающиеся работы со шрифтами, могут ввести
разработчика в заблуждение. Так, например, если вы попытаетесь использовать ло-
1 0 . 5 . Использование локальных шрифтов 379

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


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

Внимание!

Попытка использовать локальный шрифт без предварительного


поиска приведет к тому, что будет выбран шрифт по умолчанию. По­
иск необходимо выполнять лишь один раз при создании нового объ­
екта Font, W
Обратите внимание на то, что метод g e t A l l F o n t s возвращает массив объектов
Font, которые вы можете использовать так же, как и любой другой объект шрифта,
однако этот метод выполняется очень медленно. Если вам необходимо указать, что
все локальные шрифты должны быть доступны, следует использовать метод
getAvailableFontFamilyNames.
Наилучший подход— вызывать метод getAvailableFontFamilyNames в цикле,
проверяя на совпадение с требуемым именем. Кроме того, желательно иметь не­
сколько шрифтов в резерве на случай, если нужный шрифт окажется недоступным.
Если вы передаете конструктору Font имя недоступного шрифта, будет выбран
шрифт, используемый по умолчанию (SansSerif). В листинге 10.8 представлен код,
выполняющий перебор всех шрифтов, доступных на конкретной платформе.

Листинг 10.8. ListFonts.Java

import j a v a . a w t . * ;
/** Вывод имен доступных шрифтов. */
public class ListFonts {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
GraphicsEnvironment env =
GraphicsEnvironment.getLocalGraphicsEnvironment();
S t r i n g [ ] fontNames = env.getAvailableFontFamilyNames();
System.out.println("Available Fonts:");
f o r ( i n t 1=0; i<fontNames.length; i++)
S y s t e m . o u t . p r i n t l n ( " " + fontNames[i]);
}

Код, представленный в листинге 10.9, проверяет шрифты, доступные в системе, и


перед выводом строки "Java 2D" устанавливает шрифт Goudy Handtooled ВТ. Резуль­
тат выполнения кода показан на рис. 10.5. Если на платформе отсутствует Goudy
Handtooled ВТ, текст по умолчанию отображается шрифтом SansSerif.
380 Глава 10. Java 2D: графика в Java 2

Листинг 1 0 . 9 . F o n t E x a m p l e . J a v a

import java.awt.*;

/** Пример использования локальных шрифтов для вывода в


* Java 2D.
V
public class FontExample extends GradientPaintExample {
public FontExample() {
GraphicsEnvironment env ==
GraphicsEnvironment.getLocalGraphicsEnvironment();
env. getAvailableFontFamilyNames () ;
setFont(new Font("Goudy Handtooled ВТ", Font.PLAIN, 100))/
}
protected void drawBigString(Graphics2D g2d) {
g2d.setPaint(Color.black) ;
g2d.drawstring("Java 2D", 25, 215);
}

public void paintComponent(Graphics g) {


clear(g);
Graphics2D g2d = (Graphics2D)g;
drawGradientCircle(g2d) ;
drawBigString(g2d) ;
}
public static void main(String[] args) {
WindowUtilities.OpenlnJFrame(new FontExample(), 380, 400);
}

\шшшшшвшшшшшл MHHBHHaiMiiHi'

nfava 211
ovi^fl^HHr 1
Рис. 10.5. Java 2D позволяет отображать
текст любым из шрифтов, установленных
на локальном компьютере
10.6. Стили пера 381

10.6. Стили пера


В AWT вызов метода drawXxx класса G r a p h i c s приводит к отображению линии
толщиной 1 пиксель. П р и выводе изображений, состоящих из нескольких линий
(например, d r a w R e c t и d r a w P o l y g o n ) , соединение отрезков и оформление концов
линий производится единственным заранее заданным способом. Java 2D предостав­
ляет разработчику гораздо более гибкие средства создания рисунков. В дополнение к
установке цвета пера и шаблона (как вы знаете из предыдущего раздела, это осущест­
вляется с помощью метода s e t P a i n t ) Java 2D позволяет устанавливать толщину пера,
задавать рисование штриховой линией, а также определять способы соединения раз­
личных отрезков. Для управления рисованием линий следует создать объект
B a s i c S t r o k e , а затем с помощью метода s e t S t r o k e сообщить G r a p h i c s 2 D о том,
что объект B a s i c S t r o k e должен быть использован в процессе рисования.

Атрибуты пера
Объекты, передаваемые в качестве параметров методу s e t S t r o k e , должны ис­
пользовать и н т е р ф е й с S t r o k e . Такому требованию отвечает класс B a s i c S t r o k e .
Ниже описаны конструкторы B a s i c S t r o k e .

p u b l i c BasicStrokeO
Данный конструктор создает объект B a s i c S t r o k e с толщиной пера 1.0. По умол­
чанию устанавливаются стиль оформления конца отрезка CAPSQUARE и стиль со­
единения л и н и й JOIN_MITER. П р и м е р ы рисования линиями различной ширины,
оформления концов отрезков и соединения линий будут приведены ниже.

p u b l i c BasicStroke(float penWidth)
Этот конструктор создает объект B a s i c S t r o k e с указанной толщиной пера. Как и
в предыдущем случае, по умолчанию устанавливаются стили (CAP_SQUARE и
JOIN_MITER).

p u b l i c BasicStroke(float p e n W i d t h , int capStyle, int joinStyle)


Данный конструктор создает объект B a s i c S t r o k e с указанными толщиной пера,
стилем оформления концов отрезков и стилем соединения линий. В качестве сти­
лей рисования концов отрезков могут быть заданы CAP_SQUARE (окончание линии
имеет прямоугольную форму и выступает за конечную точку на половину толщины
пера; этот стиль используется по умолчанию), CAP_BUTT (отрезок оканчивается в
конечной точке; такой стиль часто применяется для штриховых линий) и
CAP_ROUND (линия оканчивается скруглением с центром в конечной точке и ра­
диусом, равным половине толщины пера). Для соединения линий могут быть вы­
браны стили JOIN_MITER (внешние контуры линий продолжаются до тех пор, по­
ка они не встретятся; данный стиль применяется по умолчанию), JOIN_BEVEL
(внешние углы соединяются прямой линией) и JOIN_ROUND (соединение со скруг­
лением, диаметр которого равен толщине пера).
382 Глава 10. Java 2D: графика в Java 2

public BasicStroke(float penWidth, int capStyle, int joinStyle, float miterLimit)


Данный конструктор действует так же, как и предыдущий, за исключением того, что
он позволяет явно указывать, насколько могут продолжаться внешние контуры ли­
ний (параметр m i t e r L i m i t ) . По умолчанию используется значение m i t e r L i m i t ,
равное 10.0, поэтому данный конструктор применяется редко.
public BasicStroke(float penWidth, int capStyle, int joinStyle,
float miterLimit, float[] dashPattern,
float dashOffset)
Этот конструктор дает возможность создавать штриховые линии, задавая массив
отрезков. Четные элементы массива задают непрозрачные отрезки, а нечетные —
прозрачные. Смещение (dashOffset), которое обычно задается как 0.0, указыва­
ет начало штрихового шаблона.
Ниже приведены два примера использования атрибутов пера. В первом примере
(листинг 10.10) устанавливается толщина пера 8 пикселей, а во втором (листинг 10.11)
рисуется штриховая линия. Штриховой шаблон задается следующим образом:
f l o a t [ ] d a s h P a t t e r n { 30, 10, 10, 10 };
Здесь штрихи имеют различную длину. Сначала отображается непрозрачный от­
резок длиной 30 единиц, затем— прозрачная линия, т.е. пропуск 10 единиц, после
этого следует непрозрачный отрезок длиной 10 единиц и пропуск 10 единиц. По мере
рисования отрезка шаблон повторяется. Результаты выполнения фрагментов кода,
приведенных в листингах, показаны соответственно на рис. 10.6 и 10.7.

Листинг 10.10.StrokeThicknessExample.Java
import j a v a . a w t . * ;
/** Пример управления толщиной пера.

p u b l i c c l a s s StrokeThicknessExample extends FontExample


p u b l i c void paintComponent(Graphics g) {
clear(g) ;
Graphics2D g2d = (Graphics2D)g;
drawGradientCircle(g2d) ;
drawBigString(g2d);
drawThickCircleOutline(g2d) ;

p r o t e c t e d void drawThickCircleOutline(Graphics2D g2d) {


g2d.setPaint(Color.blue);
g2d.setstroke(new B a s i c S t r o k e ( 8 ) ) ; / / Толщина 8 пикселей
g2d.draw(getCircle());
}

p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
WindowUtilities.openlnJFrame(new StrokeThicknessExample(),
380, 400);
}
10.6. Стили пера 383

Рис. 10.6. Окружность, нарисованная пером


толщиной 8 пикселей

Листинг 1 о. 1 1 . DashedStrokeExample Java

import java.awt.*;

/** Пример создания штриховой линии.

public class DashedStrokeExample extends FontExample {


public void paintComponent(Graphics g) {
clear(g);
Graphics2D g2d = (Graphics2D)g;
drawGradientCircle(g2d);
drawBigString(g2d);
drawDashedCi rcleOutline(g2d);

protected void drawDashedCircleOutline(Graphics2D g2d) {


g2d.setPaint(Color.blue);
// Линия длиной 30 пикселей, пропуск 10 пикселей,
// линия — 10 пикселей, пропуск — 10 пикселей.
float[] dashPattern = { 30, 10, 10, 10 };
g2d.setstroke(new BasicStroke(8, Basicstroke.CAP__BUTT,
BasicStroke . JOIN_JdITER, 10 ,
dashPattern, 0));
g2d.draw(getCircle());
}

public static void main(String[] args) {


WindowUtilities.openlnJFrame(new DashedStrokeExample()
380, 400);
384 Глава 10. Java 2D: графика в Java 2

Рис. 10.7, Окружность, нарисованная


штриховой линией

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


ставлен код, демонстрирующий различные стили соединения линий и оформления
концов отрезков. Н а рис. 10.8 показаны различия между стилями JOIN_MITER,
JOIN_BEVEL и JOIN_ROUND, а также между стилями CAP_SQUARE, CAP_BUTT и
САР ROUND.

Листинг 10.12.LineStyles.j ava

import javax.swing.*;
import java.awt.*;
import Java.awt.geom.*;
/** Примеры различных способов соединений отрезков.
* Внешний вид концов линий задается параметром capStyle.

public class LineStyles extends JPanel {


private GeneralPath path;
private static int x = 30, deltaX = 150, у = 300,
deltaY = 250, thickness = 40;
private Circle plLarge, plSmall, p2Large, p2Small,
p3Large, p3Small;
private int compositeType = AlphaComposite.SRC_OVER;
private AlphaComposite transparentComposite =
AlphaComposite.getlnstance(compositeType, 0.4F);
private int[] caps =
{ BasicStroke.CAP_SQUARE, BasicStroke.CAP_BUTT,
BasicStroke.CAP_ROUND };
private String[] capNames =
{ "CAP_SQUARE", "CAP_BUTT", "CAP_ROUND" };
private int[] joins =
{ BasicStroke.JOIN_MITER, BasicStroke.JOIN_BEVEL,
BasicStroke.JOIN_ROUND };
private String[] joinNames =
{ "JOIN MITER", "JOIN BEVEL", "JOIN ROUND" };
10.6. Стили пера 385

public LineStylesO {
path = new GeneralPath();
path.moveTo(x, y ) ;
plLarge = new Circle(x, у thickness/2);
plSmall = new Circle(x, у 2);
path.lineTo(x + deltaX, у - deltaY);
p2Large = new Circle(x + deltaX, у - deltaY, thickness/2);
p2Small = new Circle(x + deltaX, у deltaY, 2 ) ;
path.lineTo(x + 2*deltaX, y ) ;
p3Large = new Circle(x + 2*deltaX, thickness/2)
p3Small = new Circle(x + 2*deltaX, 2);
setFont(new Font("SansSerif", Font.BOLD, 20));

public void paintComponent(Graphics g) {


super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.lightGray) ;
for(int i=0; i<caps.length; i++) {
BasicStroke stroke =
new BasicStroke(thickness, caps[i], joins[i]);
g2d.setStroke(stroke);
g2d.draw(path);
labelEndPoints(g2d, capNames[i], joinNames[i]);
g2d.translate(3*x + 2*deltaX, 0) ;
}
}

// Отображение подсвеченных кругов для демонстрации


// способов оформления концов линий.
private void labelEndPoints(Graphics2D g2d. String capLabel,
String joinLabel) {

Paint origPaint = g2d.getPaint();


Composite origComposite = g2d.getComposite();
g2d.setPaint(Color.black);
g2d.setComposite(transparentComposite);
g2d.fill(plLarge);
g2d.fill(p2Large);
g2d.fill(pBLarge);
g2d.setPaint(Color.yellow);
g2d.setComposite(origComposite);
g2d.fill(plSmall);
g2d.fill(p2Small);
g2d.fill(p3Small);
g2d.setPaint(Color.black);
g2d.drawstring(capLabel, x + thickness - 5, у + 5 ) ;
g2d.drawstring(joinLabel, x + deltaX + thickness - 5,
у - deltaY);
g2d.setPaint(origPaint);
}

public static void main(String[] args) {


WindowUtilities.openlnJFrame(new LineStyles(),
9*x + 6*deltaX, у + 60)
386 Глава 10. Java 2D: графика в Java 2

}
}

class. Circle extends Ellipse2D.Double {


public Circle(double centerX, double centerY, double radius) {
super(centerX - radius, centerY - radius, 2.0*radius,
2.0^radius);
}
}

JOIN_MITER ^ JOIN.BEVEL J | JOIN_ROUND

CAP SQUARE Ш1 Ш CAP_BUTT gR в | CAP.ROUND

Рис. 10.8. Примеры использования различных стилей объединения линий и оформления


концов отрезков

10.7. Преобразование координат


Java 2D позволяет преобразовывать текущую систему координат: выполнять сдвиг,
вращение и масштабирование (в том числе нелинейное). Часто выполнить преобра­
зование координат бывает гораздо проще, чем вычислять координаты точек в старой
системе. Более того, единственный способ отобразить некоторые фигуры, например
эллипсы, повернутыми на некоторый угол, или представить в другом масштабе — это
выполнить соответствующее преобразование координат. Смысл сдвига, вращения и
масштабирования системы координат интуитивно ясен. С нелинейным масштабиро­
ванием (shear) дело обстоит несколько сложнее. П р и нелинейном масштабировании
перемещение точки по координате х вправо зависит от ее расположения по оси у.
Аналогично, перемещение вниз по координате у зависит от расположения по оси х.
Для того чтобы лучше понять, что происходит при преобразовании координат,
представьте себе такую картину: перед художником лежит лист бумаги (на нем он со­
бирается рисовать), на котором размещена рамка. Первоначально верхний левый
край рамки совмещен с верхним левым краем листа. П р и сдвиге рамка сдвигается от­
носительно листа, соответственно художник меняет свое положение и рисует внутри
рамки фрагмент изображения. Затем рамку возвращают в исходное положение, и по­
лученное изображение представляет собой результат рисования со сдвигом коорди­
нат. П р и вращении рамка вращается относительно листа и художник располагается
так, чтобы перед ним находился нижний край рамки. После рисования, чтобы уви­
деть результаты, надо снова вернуть рамку в исходное положение. П р и масштабиро­
вании (в том числе нелинейном) преобразования снова выполняются над рамкой;
лист бумаги остается неподвижным.
10.7. Преобразование координат 387

С точки зрения внешнего наблюдателя перемещается рамка, а лист бумаги остает­


ся неподвижным. С точки зрения художника, выполняющего рисование, рамка оста­
ется неподвижной, а лист бумаги перемещается в направлении, противоположном
указанному.
При выполнении сложных преобразований координаты точек вычисляются с по­
мощью специальной матрицы. Результаты такого преобразования не столь очевидны,
как результаты сдвига, вращения и масштабирования. Новые координаты точки (х^,
У2) вычисляются на основе текущих координат (Xj, y j следующим образом:

ж/ Шоо Шо, Шог! W


У2 = m,oX,+m,,>;,+m,2
ЩоЩ1 Щ2
г' =
1 0 0 1 J [l _ 1

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


компонентов матрицы (т,^). К о э ф ф и ц и е н т ы m^^g и т,2 обеспечивают сдвиг системы
координат. Остальные четыре к о э ф ф и ц и е н т а (т,,,,, m„j, HIJ,, И гПц) отвечают за враще­
ние. Для того чтобы при преобразованиях сохранялась ортогональность координат,
якобиан матрицы должен быть равен 1. Н и ж н я я строка матрицы имеет вид [0 0 1]; в
результате при вращении фигура остается в плоскости х-у (компонент z равен 0). Пе­
редать матрицу преобразования конструктору A f f i n e T r a n s f o r m можно нескольки­
ми способами. Подробно об этом сказано в документации по использованию API в
разделе, посвященном Aff i n e T r a n s f o r m .
Преобразования можно выполнять двумя способами: путем создания объекта
Aff i n e T r a n s f o r m и вызовом основных методов преобразования. Используя первый
из указанных подходов, вы создаете объект Aff i n e T r a n s f o r m , задаете параметры
объекта, связываете Aff i n e T r a n s f o r m с G r a p h i c s 2 D , используя для этого метод
s e t T r a n s f o r m , а затем рисуете форму (объект S h a p e ) . Вы также можете использо­
вать Aff i n e T r a n s f o r m для создания преобразованного объекта S h a p e . Для этого на­
до вызвать метод c r e a t e T r a n s f o r m e d S h a p e объекта Aff i n e T r a n s f o r m . Если необ­
ходимо выполнить сложные преобразования, лучше всего сделать это с помощью
объекта Aff i n e T r a n s f o r m , поскольку в данном случае вы явным образом определяе­
те матрицу преобразования.

На заметку
Объект Shape следует преобразовывать перед рисованием. Метод
createTransformedShape Aff IneTransform создает новый объект
Shape, преобразованный в соответствии с правилами, заданными с
помощью объекта Aff IneTransform.

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


средственно вызывая методы t r a n s l a t e , r o t a t e , s c a l e и s h e a r объекта
G r a p h i c s 2D. Преобразования, выполняемые над объектом G r a p h i c s 2 D , накаплива­
ются; каждый из методов преобразования применяется к результатам, полученным
при выполнении метода, вызванного ранее. Например, вызвав два раза подряд метод
r o t a t e ( M a t h . P I / 2 ) , вы получите тот же результат, что и при однократном вызове
388 Глава 10. Java 2D: графика в Java 2

r o t a t e (Math. P I ) . Если вы хотите вернуться к первоначальному состоянию системы


координат, сохраните контекст G r a p h i c s 2D, вызвав метод getTransform, выполни­
те операции, связанные с преобразованием координат, а затем вернитесь к сохра­
ненному кoнтeкcт)^ вызвав s e t T r a n s f o r m . Например:
/ / Сохранение текущего состояния графического контекста.
AffineTransform transform = g 2 d . g e t T r a n s f o r m ( ) ;
/ / Выполнение инкрементных преобразований,
translate(...);
rotate(...);
/ / Восстановление первоначального состояния
/ / графического контекста.
g2d.setTransform(transform);
В листинге 10.13 показан пример последовательно выполняемых операций вра­
щения системы координат. После выполнения очередной операции вращения выво­
дится строка "Java". Результаты работы программы показаны на рис. 10.9.

Листинг 10.13.RotationExample.Java

import j a v a . a w t . * ;
/** Пример преобразования системы координат с последующим
* рисованием.

public class RotationExample extends StrokeThicknessExample {


private Color[] colors = { Color.white, Color.black };

public void paintComponent(Graphics g) {


clear(g);
Graphics2D g2d = (Graphics2D)g;
drawGradientCircle(g2d);
drawThickCircleOutline(g2d);
// Перемещение начала координат в центр круга.
g2d.translate(185.О, 185.0) ;
for (int i=0; i<16; i++) {
// Вращение системы координат относительно ее начала.
// Начало координат совпадает с центром круга.
g2d.rotate(Math.PI/8.0);
g2d.setPaint(colors[i%2]);
g2d.drawstring("Java", 0, 0 ) ;
}
}

public static void main(String[] args) {


WindowUtilities.openlnJFrame(new RotationExample(), 380, 400);
10.7. Преобразование координат 389

Рис. 10.9. Пример сдвига и вращения


системы координат

Нелинейное масштабирование
п р и нелинейном масштабировании система координат "растягивается" параллельно
осям. Если вы зададите ненулевое значение х-масштабирования, координата х сдвигает­
ся тем больше, чем дальше точка отстоит от начала координат по оси у. Например, зна­
чение х-масштабирования, равное 0.1, указывает на то, что координата х сдвигается на
10% от расстояния по оси у. Аналогично, сдвиг координаты у пропорционален расстоя­
нию по оси X. И х- и у-масштабирование могут задаваться одновременно.
Лучше всего продемонстрировать нелинейное масштабирование на конкретном
примере. Код примера приведен в листинге 10.14, а результаты его выполнения пока­
заны на рис. 10.10. Здесь значение х-масштабирования возрастает от 0.0 для первого
квадрата до +0.8 для пятого квадрата. Значение у-масштабирования остается неиз­
менным.

Листинг 1 0 . 1 4 . S h e a r E x a m p l e . j a v a

import javax.swing.^;
import java.awt.*;
import Java.awt.geom.*;

/ * * Пример нелинейного масштабирования геометрической фигуры. * /

p u b l i c c l a s s ShearExample extends JPanel {


p r i v a t e s t a t i c i n t gap=10, width=100;
p r i v a t e R e c t a n g l e r e c t = new R e c t a n g l e ( g a p , gap, 100, 100);

p u b l i c void paintComponent(Graphics g) {
s u p e r . paintComponent (g). ;
Graphics2D g2d = (Graphics2D)g;
for ( i n t i = 0 ; i < 5 ; i++) {
g2d.setPaint(Color.red);
g2d.fill(rect) ;
// Для- каждого нового квадрата значение
// х-масштабирования увеличивается на 0.2.
390 Глава 10. Java 2D: графика в Java 2

g2d.shear(0.2, 0.0);
g2d.translate(2*gap + width, 0) ;

public static void main(String[] args) {


String title =
"Shear: x shear.ranges from 0.0 for the leftmost" +
"'square' to 0.8 for the rightmost one.";
WindowUtilities.openlnJFrame(new ShearExample(),
20*gap + 5*width,
5*gap + width,
title);
}

es from 0.0 for the ieftmust 'so

Рис. 10.10. При положительном значении х-масштабирования сдвиг по


координате х возрастает при увеличении координаты у (как вы знаете,
значение координаты у возрастает при движении сверху вниз)

10.8. Прочие возможности Java 2D


По сравнению с AWT Java 2D выполняет большой объем вычислений, поэтому по
умолчанию некоторые дополнительные средства отключены. Однако для вывода вы­
сококачественных изображений, особенно при отображении повернутого текста, не­
обходимо разрешить поддержку некоторых средств.
Наиболее важными установками являются сглаживание (устранение неровностей
при рисовании линий путем выбора цвета) и вывод высококачественной графики.
Эти установки демонстрирует следующий фрагмент кода:
RenderingHints renderHints =
new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
renderHints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);

public void paintComponent(Graphics g)


super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHints(renderHints);

}
10.8. Прочие возможности Java 2 D 391

М е т о д и к а профессионалов

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


RenderxngHlnts, ВКЛЮЧИТЬ режим сглаживания (VALUEJMUTIALIASJDIH)
и установить режим обеспечения качества за счет быстродействия
(VALUE_RENDER_QUALITY).

В этой главе мы лишь вкратце рассмотрели основные возможности, предостав­


ляемые Java 2D. После того как вы приобретете опыт их использования, можете при­
ступать к изучению дополнительных средств, которые позволят вам выполнять сле­
дующие действия.
• Поддерживать произвольно выбранные режимы смешивания цветов (реализо-
вывать интерфейсы Composite и CompositeContext).
• Контролировать соотношение границ (используя методы contains и
i n t e r s e c t s объекта Shape).
• Создавать новые шрифты на базе имеющихся (с помощью F o n t . d e r i v e F o n t ) .
• Отображать строки текста различным цветом и различными шрифтами
(используя метод draw объекта TextLayout).
• Отображать контуры символов, а также заполнять символы изображениями или
градиентным цветом (используя метод g e t O u t l i n e объекта TextLayout).
• Осуществлять низкоуровневую обработку изображения либо выполнять раз­
личные действия с цветовой моделью.
• Реализовывать высококачественную печать Swing-компонентов (подробно этот
вопрос будет рассмотрен в разделе 15.5).

10.9. Резюме
Java 2D предоставляет разработчику средства для вывода высококачественных гра­
фических изображений. В частности, Java 2D обеспечивает следующие возможности.
• Рисование контуров и заполнение произвольного объекта Shape. Для этого
используются методы draw и f i l l объекта Graphics2D. В качестве параметра
данным методам передается объект Shape.
• Применение метода s e t P a i n t класса Graphics2D для отображения форм од­
ним цветом (Color), реализации градиентного заполнения ( G r a d i e n t P a i n t )
или заполнения повторяющимися изображениями ( T e x t u r e P a i n t ) .
• Реализация прозрачных форм и изменение правил объединения цветов при
отображении нескольких форм. Правила объединения, реализованные в классе
AlhpaComposite, определяют отображение форм на имеющемся фоне.
• Преодоление ограничения, согласно которому вы можете рисовать лишь пе­
ром толщиной в один пиксель и создавать объекты B a s i c S t r o k e , позволяю­
щие управлять толщиной пера, отображать штриховые линии и задавать пра­
вила соединения отрезков.
392 Глава 10. Java 2D: графика в Java 2

• Создание объектов Af f i n e T r a n s f o r m и вызов метода s e t T r a n s f o r m объекта


G r a p h i c s 2 D для осуществления сдвига, вращения, масштабирования (в том
числе нелинейного) форм перед выводом.
• Контроль качества изображения с использованием RenderingHints.
R e n d e r i n g H i n t s позволяет также управлять сглаживанием.
Следует помнить, что Java 2D является частью Java Foundation Classes и может ис­
пользоваться только на платформе Java 2. Набор "легковесных" компонентов Swing
также входит в состав Java Foundation Classes. Компоненты Swing подробно рассмат­
риваются в главах 14 и 15.
Отображение ф о р м и текста, а также вывод растровых изображений необходимы
для работы многих приложений. Однако эти возможности недостаточны для созда­
ния графического интерфейса, обеспечивающего взаимодействие с пользователем.
При реализации интерфейса необходимо создавать различные типы окон и включать
в них управляющие элементы, такие как кнопки, поля редактирования и др. Пробле­
мы создания пользовательских интерфейсов рассмотрены в следующих трех главах.
СОБЫТИЯ,
СВЯЗАННЫЕ
С МЫШЬЮ
И КЛАВИАТУРОЙ

В ЭТОЙ главе...

Общий подход к обработке событий.

Использование отдельного обработчика событий.

Реализация интерфейса обработчика.

Обработка событий с помощью внутренних классов.

Стандартные обработчики событий.

Обработка событий с помощью методов processXxxEvent

Поле редактирования с проверкой орфографии.

Реализация "чертежной доски".


S~y\zJ^zJ

Н
екоторые действия пользователя, например щелчок кнопкой мыши, нажатие
клавиши на клавиатуре, перемещение окна, классифицируются как события.
Обработка событий в Java не предполагает явной проверки состояния соот­
ветствующих устройств или объектов. Вместо этого вы сообщаете системе приблизи­
тельно следующее: "Если произойдет событие определенного типа, связанное с дан­
ным окном, об этом следует оповестить указанный объект". Получив такое сообще­
ние, система ожидает возникновения событий указанного типа и оповещает об этом
специальные объекты, называемые объектами прослушивания, или обработчиками собы­
тий. Обработка событий включает следующие действия.
1. Определение типа интересующего вас события.
Существует 11 типов объектов прослушивания AWT, которые перечислены в
табл. 1.1. Выберите объект, соответствующий действиям, которые вы хотите
отслеживать. Например, объект K e y L i s t e n e r соответствует нажатию и отпус­
канию клавиш клавиатуры, объект M o u s e L i s t e n e r — действиям пользователя с
кнопками мыши, объект F o c u s L i s t e n e r позволяет выявлять события, связан­
ные с получением и потерей фокуса элементами пользовательского интерфей­
са и т. д.
2. Создание класса требуемого типа.
Один из способов решения данной задачи состоит в непосредственной реали­
зации классом одного из интерфейсов прослушивания ( M o u s e L i s t e n e r ,
K e y L i s t e n e r , F o c u s L i s t e n e r и т.д.) В создаваемом классе надо создать мето­
ды, соответствующие конкретной подкатегории интересующих вас событий.
Так, например, если вам необходимо поддерживать нажатие и отпускание
кнопки мыши, надо реализовать интерфейс M o u s e L i s t e n e r , в частности раз­
работать методы, соответствующие этим событиям. Если интерфейс включает
больше одного метода, вы можете воспользоваться классом адаптера, в кото­
ром реализованы все методы. Реализация этих методов не выполняет никаких
действий, поэтому интересующие вас методы следует переопределить. Адапте­
ры подробно обсуждаются ниже в этой главе.
396 Глава 1 1 . События, связанные с мышью и клавиатурой

S. Регистрация объекта прослушивания.


Каждому типу объекта прослушивания SomethingLlstener соответствует метод
addSom^/Am^Listener, используемый для регистрации обработчика события.
Так, например, если вам надо, чтобы события в окне w, имеющие отношение к
действиям с кнопкой мыши, обрабатывались экземпляром класса MyMouse-
L i s t e n e r , включите в свою программу команду w . a d d M o u s e L i s t e n e r (new
MyMouseListener());.
Существует четыре способа определения класса, обрабатывающего события.
Вы можете указать в качестве обработчика отдельный объект, использовать для этого
существующий объект, реализующий интерфейс прослушивания, либо последовать
одному из двух подходов, связанных с применением внутренних классов. Эти четыре
способа описаны в последующих разделах.

1 1 . 1 . Поддержка событий посредством


отдельного обработчика
Предположим, что вам необходимо создать аплет, который в ответ на щелчок мыши
в выделенной для него области выводил бы на Java-консоли координаты курсора мыши.
(Как вы помните, для отображения окна Java Console надо воспользоваться меню
Communicator или Communicator/Tools броузера Netscape либо меню View броузера
Internet Explorer.) Для того чтобы выполнить эту задачу, вам надо ознакомиться с типа­
ми объектов прослушивания. Сделав это, вы увидите, что интерфейс MouseListener со­
ответствует действиям с кнопкой мыши, а класс M o u s e A d a p t e r предоставляет "пустые"
реализации методов M o u s e L i s t e n e r . Изучив интерфейс M o u s e L i s t e n e r , вы также уз­
наете, что метод m o u s e P r e s s e d вызывается в ответ на первое нажатие кнопки мыши и
в качестве параметра этому методу передается экземпляр класса MouseEvent. В классе
MouseEvent содержатся методы g e t X и g e t Y , которые возвращают координаты курсо­
ра мыши относительно верхнего левого угла окна в момент нажатия кнопки. Таким об­
разом, вы можете реализовать обработчик, подобный представленному в листинге 11.1,
и связать его с аплетом, как это показано в листинге 11.2. Результаты представлены на
рис. 11.1. HTML-файл, включающий данный аплет, вы найдете среди исходных кодов по
адресу h t t p : / / w w w . c o r e w e b p r o g r a m m i n g . c o m / .

Листинг 1 1 . 1 . CiickListener.java

import Java.awt.event.*;
/ * * Обработчик событий, используемый C l i c k R e p o r t e r . */

p u b l i c c l a s s C l i c k L i s t e n e r e x t e n d s MouseAdapter {
p u b l i c void mousePressed(MouseEvent event) {
S y s t e m . o u t . p r i n t l n ( " M o u s e p r e s s e d a t (" +
event. getXO + " , " +
event.getY0 + " ) . " ) ;
}
}
11.1. Поддержка событий. 397

Листинг 11.2. ClickReporter.Java

import Java, applet .Applets-


import java.awt.*;

/^* Вывод сообщения о действиях пользователя (щелчке


* мышью). Используется внешний обработчик.

public class ClickReporter extends Applet {


public void initO {
setBackground(Color.yellow);
addMouseListener(new ClickListener());

iS iiwupai'rrpummiffmi
fife £{fe Vtew F^vontes Tods Иёр

•>-• J Jib UJJ^'.&-^J-


~3
Reporting Mouse Clicks

l-.lii.lAJ.i!. fifxil
JMouse p r e s s e d at ( 2 6 , 2 2 ) .
jMouse p r e s s e d at ( 6 5 , 6 3 ) ,
jMouse p r e s s e d at (110,106)
;House p r e s s e d ac (160,137)
jMouse p r e s s e d at ( 2 1 0 , 1 7 1 ) .
JKouse p r e s s e d at (275,211)
JHouse p r e s s e d at ( 3 6 7 , 2 7 4 ) .
Mouse pressed at ( 3 9 0 , 3 2 5 ) .
JMouse p r e s s e d at ( 4 2 6 , 3 2 7 ) .

; ^ iiwtel «tarted ^ My CoTfipute»


J Li
Clear
_H
CJose

Рис. 11.1. Так выглядит аплет C l i c k R e p o r t e r после того, как поль^зователь


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

Отображение кругов
Теперь изменим код аплета так, чтобы вместо вывода координат курсора мыши он
отображал круг с центром в той точке, в которой находился курсор в момент нажатия
кнопки. Получить объект G r a p h i c s за пределами метода paint довольно просто: для
этого достаточно вызвать метод g e t G r a p h i c s . Проблема состоит в том, что обра­
ботчик событий не принадлежит классу Applet и, следовательно, не содержит метод
398 Глава 1 1 . События, связанные с мышью и клавиатурой

g e t G r a p h i c s . Разрешить эту проблему помогает метод g e t S o u r c e (определенный


для всех типов событий), предоставляющий ссылку на окно, с которым связано собы­
тие. Эту ссылку можно привести к типу Applet и вызвать метод g e t G r a p h i c s . Код,
реализующий описанный подход, представлен в листингах 11.3 и 11.4, а результаты
выполнения примера показаны на рис. 11.2.
Методика профессионалов

Вызов event. getSource () позволяет получить ссылку на окно, с ко­


торым связано событие.

Листинг 1 1 . 3 . C i r c l e L i s t e n e r . j a v a

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
import J a v a . a w t . e v e n t . * ;
/** Обработчик, используемый C i r c l e D r a w e r l . Для
* получения ссылки на аплет вызывается метод getSource.
V
p u b l i c c l a s s C i r c l e L i s t e n e r extends MouseAdapter {
p r i v a t e i n t r a d i u s = 25;
p u b l i c void mousePressed(MouseEvent event) {
Applet app = ( A p p l e t ) e v e n t . g e t S o u r c e 0 ;
Graphics g = app.getGraphics ( ) ;
g.fillOval(event.getX()-radius,
event.getY()-radius,
2*radius,
2*radius);

Листинг 11.4. CircleDrawer.java

import Java.applet.Applet;
import java.awt.*;

/•• Рисование кругов с центром в точке, на которую указывает


* курсор мыши. Используется внешний обработчик.
V
public class CircleDrawerl extends Applet {
public void initO {
setForeground(Color.blue);
addMouseListener(new CircleListener());
}
}
11.2. Поддержка событий путем. 399

Ш Using External Li«tefie(s fm Event Hanf№ng - Netscape

d
Using External Listeners for Event Handling
Click ill the applet to di aw circles.

ШР^Г If^ifMC«deOtaw«1 tunn :.' -"^ - 3 ^ .'^^, 'Ш ,^A

Рис, 11.2. Внешний обработчик может вызывать


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

11.2. Поддержка событий путем реализации


интерфейса прослушивания
В предыдущем примере обработчик события был полностью отделен от исполь­
зующего его аплета. Несмотря на то что в некоторых ситуациях подобное разделение
упрощает повторное использование объектов, в данной ситуации оно вызывает до­
полнительные трудности при вызове методов аплета из обработчика. Прежде всего,
получать ссылку с помощью метода g e t S o u r c e и выполнять преобразование типов
только для того, чтобы иметь дост)^п к объекту, не совсем удобно. Кроме того, ссылка,
полученная таким образом, позволяет вызывать лишь общедоступные методы. Мето­
ды, определенные как p r i v a t e и p r o t e c t e d , остаются недоступными.
Существует другой подход — объявить в качестве обработчика событий мыши сам
аплет. Чтобы не нарушить строгие правила Java, определяющие работу с типами, надо
указать, что аплет реализует и н т е р ф е й с M o u s e L i s t e n e r . Реализация интерфейса —
это своеобразное "обещание" компилятору включить в состав класса определенные
методы. В данном случае, объявляя аплет как класс, реализующий интерфейс M o u s e -
L i s t e n e r , разработчик берет на себя обязательство создать методы m o u s e E n t e r e d ,
m o u s e E x i t e d , m o u s e P r e s s e d , mouseReleased и m o u s e C l i c k e d .
В листинге 11.5 приведен код аплета, который, следуя описанному подходу, выпол­
няет те же действия, что и аплет, рассмотренный в предыдущем разделе. Теперь метод
400 Глава 1 1 . События, связанные с мышью и клавиатурой

m o u s e P r e s s e d выглядит гораздо проще, поскольку он входит в состав класса аплета и


из него можно непосредственно обратиться к методу g e t G r a p h i c s . Заметьте, что в ап-
лете должны быть реализованы все пять методов, определяемых интерфейсом, несмот­
ря на то, что реальные действия выполняются только в теле одного из них.

Листинг 1 1 . 5 . C i r c l e D r a w e r 2 . J a v a

import Java.applet.Applet;
import java.awt.*;
import Java.awt.event.*;

/ * • Рисование к р у г о в с центром в т о ч к е , на которую


* у к а з ы в а е т к у р с о р мыши. Роль о б р а б о т ч и к а выполняет
* источник события.

public class CircleDrawer2 extends Applet


implements MouseListener {
private int radius = 25;

public void init() {


setForeground(Color.blue);
addMouseListener(this);

// Остальные методы, объявленные в интерфейсе MouseListener.

public void mouseEntered(MouseEvent event) {}


public void mouseExited(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
public void mouseClicked(MouseEvent event) {}

public void mousePressed(MouseEvent event) {


Graphics g = getGraphics();
g.fillOval(event.getX()-radius,
event.getY()-radius,
2*radius,
2^radius);
}

11.3. Обработка событий с помощью


именованных внутренних icnaccoB
в первом варианте аплета, отображающего круги в ответ на нажатие кнопки мы­
ши, обработчику событий для обращения к классу A p p l e t приходилось предприни­
мать некоторые дополнительные действия. Эти действия были продиктованы требо­
ванием Java, чтобы разработчик строго придерживался принципов объектно-ориен-
ированного программирования. Во втором варианте аплета, выполняющего те же
действия, обработку событий осуществлял класс A p p l e t . Для этого класс аплета pea-
1 1 . 3 . Обработка событий с п о м о щ ь ю . . . 401

лизовывал интерфейс M o u s e L i s t e n e r , а следовательно, включал четыре "пустых"


метода, в теле которых не выполнялись никакие действия. Внутренние, или вложен­
ные, классы объединяют преимущества обоих подходов. Внутренним называется
класс, определение которого находится в составе другого класса. Он обеспечивает
полной доступ к методам и полям включающего объекта, даже если они объявлены
как p r i v a t e . Пример определения внутреннего класса приведен ниже.
public c l a s s Outer {
p r i v a t e class Inner { .., }

p r i v a t e I n n e r t e s t = new I n n e r ( ) ;

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


мый интерфейс. Переменная t h i s внутреннего класса ссылается на сам внутренний
класс. Для доступа к включающему классу используется ссылка O u t e r . t h i s . При ком­
пиляции каждый из внутренних классов оформляется в виде отдельного файла, имя ко­
торого создается на базе включающего класса (например, O u t e r $ I n n e r . c l a s s ) . Как
правило, разработчику не приходится заботиться об именах файлов классов, однако
при переносе программы в другой каталог или на другой компьютер такое соглашение
об именовании облегчает поиск файлов, соответствующих внутренним классам.

М е т о д и к а профессионалов

Перемещая файлы классов, содержащих внутренние классы, не за­


бывайте скопировать файлы, которые соответствуют внутренним
классам.

В листинге 11.6 приведена версия класса C i r c l e D r a w e r , в котором используется


именованный внутренний класс. Метод g e t G r a p h i c s вызывается непосредственно
из внутреннего класса, при этом нет необходимости вызывать e v e n t . g e t Sou г с е для
получения ссылки на аплет. Вместо того чтобы реализовывать интерфейс
M o u s e L i s t e n e r , вн)т:ренний класс оформляется как подкласс класса M o u s e A d a p t e r .
Благодаря этому внутренний класс наследует "пустые" реализации методов
mouseEntered, mouseExited,mouseReleased nmouseClicked.

Листинг 1 1 . 6 . C i r c l e D r a w e r 3 . J a v a

import Java.applet.Applet;
import java.awt.*;
import Java.awt.event.*;

/k-k Рисование к р у г о в с центром в т о ч к е , на которую


* у к а з ы в а е т к у р с о р мыши. Для обработки событий и с п о л ь з у е т с я
* внутренний к л а с с .

public class CircleDrawerS extends Applet {


public void initO {
setForeground(Color.blue);
402 Глава 1 1 . События, связанные с мышью и клавиатурой

addMouseListener(new CircleListener());
}

private class CircleListener extends MouseAdapter


private int radius = 25;

public void mousePressed(MouseEvent event) {


Graphics g = getGraphics();
g.fillOval(event.getX()-radius,
event.getY()-radius,
2*radius,
2*radius);
}
} .

11.4. Обработка событий с помощью


неименованных внутренних классов
в предыдущем примере был использован вн^тренниР! класс. Если не учитывать осо­
бенности его расположения (тот факт, что он определяется внутри другого класса), то в
остальном внутренние классы ничем не отличаются от обычных классов; при их созда­
нии применяются те же синтаксические правила. В отличие от именованных внутрен­
них классов, неименованные, или анонимные, внутренние классы позволяют опреде­
лять классы в составе выражений. Определение неименованного класса выглядит как
вызов конструктора суперкласса, за которым следует определение подкласса. Ниже
приведены два примера использования неименованных внутренних классов.

C o l o r someColor = p i c k C o l o r O ;
add(new P a n e l ( ) {
p u b l i c Color o r i g C o l o r = someColor;
public void i n i t O {
setBackground(origColor);
}
});

K e y A d a p t e r myAdapter =
new K e y A d a p t e r 0 {
p u b l i c void keyPressed(KeyEvent event) { ... }
};
addKeyListener(myAdapter);

В листинге 11.7 показано использование внутренних классов при реализации ап-


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

Листинг 11.7.CircleDrawer4.java

import Java.applet.Applet;
import java.awt.*;
import Java.awt.event.*;

/•• Рисование кругов с центром в точке, на которую


* указывает курсор мыши. Для обработки событий используется
* неименованный внутренний класс.
Ч
public class CircleDrawer4 extends Applet {
public void initO {
setForeground(Color.blue);
addMouseListener
(new MouseAdapter() {
private int radius = 25;

public void mousePressed(MouseEvent event) {


Graphics g = getGraphics();
g.fillOval(event.getX()-radius,
event.getY()-radius,
2*radius,
2*radius); ,
}
});

11.5. Стандартные обработчики событий


в табл. 11.1 перечислены 11 обработчиков событий AWT, подробно описанные в
данном разделе. Имя метода, предназначенного для регистрации обработчика события,
создается на базе имени обработчика, к которому добавляется префикс add. Так, на­
пример, метод a d d M o u s e L i s t e n e r регистрирует объект прослушивания M o u s e -
L i s t e n e r , метод a d d C o m p o n e n t L i s t e n e r — объект C o m p o n e n t L i s t e n e r и т.д. Каж­
дому из методов a d d X x x L i s t e n e r соответствует метод r e m o v e X x x L i s t e n e r . Помимо
11 классов прослушивания AWT, Swing добавляет свои обработчики, которые будут рас­
смотрены в главе, посвященной Swing. Каждому из интерфейсов прослушивания, в ко­
тором определено более одного метода, соответствует класс адаптера, содержащий
"пустые" реализации всех методов. Поскольку при написании программы вы всегда пе­
реопределяете хотя бы один метод (обработчик, не выполняющий никаких действий по
обработке событий, абсолютно бесполезен), то создавать адаптеры для интерфейсов,
содержащих только один метод, бессмысленно.
404 Глава 11. События, связанные с мышью и клавиатурой

Таблица 11.1. Обработчики событий

Обработчик Класс адаптера Регистрационный метод

ActionListener addActionListener
AdjustmentListener addAdjustmentListener
ComponentListener ComponentAdapter addComponentListener
ContainerListener ContainerAdapter addContainerListener
FocusListener FocusAdapter addFocusListener
ItemListener addltemListener
KeyListener KeyAdapter addKeyListener
MouseListener MouseAdapter addMouseListener
MouseMotionListener MouseMotionAdapter addMouseMotionListene
r
TextListener addTextListener
WindowListener WindowAdapter addWindowListener
Обработчики содержат методы, переопределив которые вы можете контролиро­
вать события. Каждому из этих методов передается единственный параметр, пред­
ставляющий собой подкласс AWTEvent. AWTEvent содержит четыре важных метода:
consume (удаление события), i s C o n s u m e d (возвращает логическое значение, опре­
деляющее, было ли событие обработано другим обработчиком), g e t ID (возвращает
целочисленное значение, представляющее тип события) и g e t S o u r c e (возвращает
объект O b j e c t , определяющий источник события).

ActionListener
Данный интерфейс определяет единственный метод:
public void actionPerformed(ActionEvent event)
Поскольку интерфейс содержит только один метод, соответствующий класс адап­
тера отсутствует. Обработчик такого типа применяется для работы с кнопками,
списками, меню и полями редактирования. Метод a c t i o n P e r f o r m e d вызывается
тогда, когда пользователь щелкает на кнопке, нажимает клавишу <Enter>, введя
текст в поле редактирования, и в других подобных случаях. Кроме стандартных
методов AWTEvent, A c t i o n E v e n t содержит два дополнительных метода:
g e t A c t i o n C o m m a n d (он возвращает "командную строку" источника в виде объекта
String) и g e t M o d i f i e r s (возвращает набор флагов, определяющих состояние кла­
виш <Shift>, <Ctrl> и т.д. П о умолчанию для кнопки "командная строка" источника
совпадает с надписью на кнопке, однако для кнопок, надписи на которых изменя­
ются, она может быть задана отдельно. В большинстве случаев наиболее важным
оказывается метод g e t S o u r c e , который позволяет определить конкретный ком­
понент, сгенерировавший событие.
11.5. Стандартные обработчики событий 405

AdjustmentListener
В составе данного интерфейса содержится один метод:
p u b l i c void adjustmentValueChanged(AdjustmentEvent event)
Класс адаптера, соответствующий A d j u s t m e n t L i s t e n e r , отсутствует. Данный объ­
ект прослушивания применяется только при работе с полосами прокрутки, и метод
a d j u s t m e n t V a l u e C h a n g e d вызывается тогда, когда состояние полосы прокрутки
изменяется. По сравнению со стандартными методами AWTEvent Ad j u s t m e n t E v e n t
содержит несколько новых методов. В частности, g e t A d j u s t a b l e возвращает ис­
ходную полосу прокрутки, g e t A d j u s t m e n t T y p e возвращает одну из констант
UNIT_DECREMENT, UNIT_INCREMENT, BLOCK_DECREMENT, BLOCK_INCREMENT или
TRACK; g e t V a l u e позволяет определить текущие установки.

ComponentListener
Интерфейс C o m p o n e n t L i s t e n e r определяет следующие четыре метода:
public void componentResized(ComponentEvent event)
public void componentMoved(ComponentEvent event)
public void componentShown(ComponentEvent event)
public void componentHidden(ComponentEvent event)
Java API предоставляет класс C o m p o n e n t A d a p t e r , содержащий "пустые'' реализа­
ции каждого из указанных методов. Используя данный класс, вы можете переоп­
ределить нужные методы и не заботиться об остальных. Перечисленные выше ме­
тоды вызываются тогда, когда размеры компонента изменяются, компонент пере­
мещается, становится видимым и исчезает из области видимости. Метод
g e t C o m p o n e n t класса C o m p o n e n t E v e n t возвращает объект Component, сгенери­
ровавший событие. Использовать метод g e t C o m p o n e n t удобнее, чем вызывать
метод g e t S o u r с е , а затем приводить возвращаемый результат к типу Component.

ContainerListener
В данном интерфейсе определены два следующих метода:
public void componentAdded(ContainerEvent event)
public void componentRemoved(ContainerEvent event)
Класс ContainerAdapter реализует оба метода. Методы ContainerListener
вызываются тогда, когда компоненты добавляются к контейнеру либо удаляются
из него. Класс ContainerEvent содержит методы getContainer и getChild для
доступа к окну и соответствующему компоненту.

FocusListener
Интерфейс FocusListener содержит два метода:
public void focusGained(FocusEvent event)
public void focusLost(FocusEvent event)
"Пустые" версии данных методов реализованы в классе F o c u s A d a p t e r . Класс
F o c u s E v e n t содержит метод i s T e m p o r a r y , который возвращает логическое значе­
ние, определяющее, является ли изменение фокуса временным или постоянным.
406 Глава 1 1 . События, связанные с мышью и клавиатурой

ItemListener
Данный интерфейс определяет единственный метод:
public void itemStateChanged(ItemEvent event)
Класс адаптера, соответствующий интерфейсу I t e m L i s t e n e r , отсутствует. Обра­
ботчик этого типа применяется при работе с объектами Checkbox,
CheckboxMenuItem, C h o i c e и L i s t . Метод вызывается тогда, когда состояние
объекта изменяется. Класс I t e m E v e n t содержит три метода: g e t l t e m S e l e c t a b l e
(определяет объект источника), g e t I t e m (определяет выбранный пункт) и
g e t S t a t e C h a n g e (возвращает целочисленное значение I t e m E v e n t . SELECTED
или I t e m E v e n t . DESELECTED).

KeyListener
Интерфейс K e y L i s t e n e r объявляет три метода:
p u b l i c void keyPressed(KeyEvent event)
p u b l i c void keyReleased(KeyEvent event)
p u b l i c v o i d keyTyped(KeyEvent event)
Класс K e y A d a p t e r содержит "пустые" варианты этих методов, таким образом, вы
можете переопределить один или два из них и не заботиться о реализации осталь­
ных. Для вызова обработчика клавиша должна быть нажата в тот момент, когда ком­
понент имеет фокус, поэтому обычные окна, такие как P a n e l или C a n v a s , должны
явно запрашивать фокус, вызывая метод r e q u e s t F o c u s . Методы k e y P r e s s e d и
k e y R e l e a s e d сл)'жат для перехвата низкоуровневых действий; в этом случае состоя­
ние клавиш-модификаторов <Shift>, <Ctrl> и др. передается отдельно. Если вы не хо­
тите, чтобы код клавиши был доступен компоненту, используйте consume в теле ме­
тода k e y P r e s s e d . Поступая таким образом, вы ограничите набор символов, кото­
рые пользователь может ввести в поле редактирования. Если вас интерес)тот только
отображаемые символы, вам надо переопределить метод keyTyped.
Класс KeyEvent определяет ряд методов и констант. При написании программы
большую помощью могут оказать два важных метода— getKeyChar и setKeyChar.
Метод getKeyChar возвращает код введенного символа, а метод s e t K e y C h a r по­
зволяет заменить один символ другим. Это позволяет заменять символы табуляции и
перевода строки пробелами, преобразовывать регистр символов и выполнять другие
подобные действия. В классе KeyEvent содержатся также методы g e t M o d i f i e r s и
s e t M o d i f i e r s , которые позволяют определять и заменять клавиши-модификаторы,
а также логический метод i s A c t i o n K e y , который дает возможность отличать функ­
циональные клавиши и клавиши управления курсором от других клавиш. Вы также
можете использовать методы, унаследованные от класса I n p u t E v e n t : isAltDown,
i s C o n t r o l D o w n , isMetaDown и i s S h i f tDown. Вместо символьных значений, полу­
чаемых с помощью метода g e t K e y C h a r , вы можете обрабатывать целочисленные
величины, возвращаемыми g e t K e y C o d e , передавая их затем методу getKeyText.
Такой подход часто используется при работе с национальными кодировками. В до­
полнение к g e t K e y C o d e в составе класса содержится также метод setKeyCode.
Объект Component, пол)^ающий событие, может быть определен с помощью мето­
да getComponent, а время нажатия клавиши возвращает метод getWhen.
11.5. Стандартные обработчики событий 407

MouseListener
Данный интерфейс объявляет следующие пять методов:
p u b l i c void mouseEntered(MouseEvent event)
p u b l i c void mouseExited(MouseEvent event)
p u b l i c void mousePressed(MouseEvent event)
p u b l i c void mouseReleased(MouseEvent event)
p u b l i c void mouseClicked(MouseEvent event)
Методы m o u s e E n t e r e d и m o u s e E x i t e d соответствуют пересечению курсором мы­
ши границы компонента, использующего обработчик. Метод m o u s e E n t e r e d вызы­
вается тогда, когда курсор мыши входит в область, занимаемую компонентом, а
m o u s e E x i t e d — при выходе курсора из этой области. Кроме того, m o u s e E x i t e d вы­
зывается в том случае, когда курсор указывает на другой компонент, который распо­
ложен поверх компонента, связанного с обработчиком. Предположим, например,
что в области, занимаемой аплетом, расположена кнопка и вы перемещаете курсор
из-за пределов аплета на кнопку, а затем снова за пределы аплета. Сначала аплет по­
лучит событие m o u s e E n t e r e d (это произойдет тогда, когда курсор пересечет грани­
цу аплета), затем— событие m o u s e E x i t e d (при помещении курсора на кнопку), по­
сле этого будет снова сгенерировано событие m o u s e E n t e r e d (когда курсор выйдет
за пределы кнопки и окажется в области аплета) и, наконец, событие m o u s e E x i t e d
(когда курсор покинет область аплета). Обратите внимание на то, что координаты х
и у, полученные в методе m o u s e E x i t e d , могут лежать за пределами аплета; более то­
го, позиция курсора, определяемая в теле m o u s e E x i t e d , часто не имеет смысла.
Метод m o u s e P r e s s e d соответствует нажатию кнопки мыши, метод m o u s e R e l e a s e d —
отпусканию кнопки, а метод m o u s e C l i c k e d — ситуации, когда между нажатием и от^
пусканием координаты мыши не изменились. Так, например, если вы нажмете и отпус­
тите кнопку, не передвигая мышь, будут вызваны методы m o u s e P r e s s e d ,
m o u s e R e l e a s e d и m o u s e C l i c k e d (в указанной последовательности). Если же вы на­
жмете кнопку, передвинете мышь, затем отпустите кнопку, будут вызваны только
m o u s e P r e s s e d и m o u s e R e l e a s e d ; метод m o u s e C l i c k e d не получит управления.
Если вы хотите переопределить некоторые (но не все) методы, применяемые для
обработки событий, вы можете использовать класс адаптера Mouse A d a p t e r . Если
вы удалите (consume) событие в методе m o u s e P r e s s e d , графический компонент
не воспримет щелчок мышью. Для того чтобы определить, какая из кнопок была
нажата, можно использовать метод g e t M o d i f i e r s . Поскольку Java-программы мо­
гут выполняться в системах, использующих однокнопочную (MacOS), двухкнопоч-
ную (Windows) или трехкнопочную (Unix) мышь, отдельные события для каждой
из кнопок в Java не предусмотрены. Значение, возвращаемое event.getModifiers(),
может быть равным M o u s e E v e n t . Button2_MASK, если нажата средняя кнопка,
или M o u s e E v e n t .Button3_MASK, если нажата правая кнопка мыши. Значение
M o u s e E v e n t . Button3_MASK соответствует правой кнопке даже в том случае, если
в системе используется двухкнопочная мышь. П р и наличии нескольких модифика­
торов метод e v e n t . g e t M o d i f i e r s () может возвращать несколько установлен­
ных флагов. Поэтому вместо проверки
(event.getModifiersО == event.ALT_MASK)
408 Глава 1 1 . События, связанные с мышью и клавиатурой

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


((event.getModifiersО & event.ALT_MASK) != 0)
Метод g e t C l i c k C o u n t позволяет отличать одиночные щелчки от многократных.
Правила, определяющие отличие двойного щелчка от двух последовательных оди­
ночных щелчков, устанавливаются не в Java, а в операционной системе. При двой­
ном щелчке метод m o u s e P r e s s e d будет вызван дважды: при первом вызове значе­
ние счетчика, возвращаемое медом e v e n t . g e t C l i c k C o u n t , буде равно 1, а при
втором вызове — 2. Как видите, система сообщает о первом щелчке, не ожидая вто­
рого. Эта стандартная процедура является причиной ошибок, допускаемых многими
начинающими разработчиками. Методы g e t X , g e t Y и g e t P o i n t позволяют узнать
координаты курсора в момент щелчка. Метод i s P o p u p T r i g g e r используется для то­
го, чтобы определить, нажал ли пользователь клавишу, специфическую для конкрет­
ной платформы, которая вызывает контекстное меню. Подобно KeyEvent,
MouseEvent наследует от I n p u t E v e n t ^ методы i s A l t D o w n , i s C o n t r o l D o w n ,
isMetaDown, i s S h i f t D o w n , g e t C o m p o n e n t и getWhen.

MouseMotionListener
В данном интерфейсе объявлены два метода:
p u b l i c v o i d mouseMoved(MouseEvent e v e n t ) —
p u b l i c v o i d mouseDragged(MouseEvent e v e n t )
"Пустые" варианты этих методов реализует класс адаптера Мои seMot i o n A d a p t e r .
Методы, принадлежащие M o u s e M o t i o n L i s t e n e r , вызываются при перемещении
мыши. Класс M o u s e E v e n t был описан при рассмотрении M o u s e L i s t e n e r .

TextListener
В составе этого интерфейса содержится единственный метод:
public void textValueChanged(TextEvent event)
Класса адаптера, соответствующего интерфейсу TextListener, не существует.
Данный обработчик применяется при работе с Text Area, Text Field и подкласса­
ми класса TextComponent. Метод textValueChanged вызывается тогда, когда
текст изменяется, независимо от того, произошло ли это изменение в результате
действий пользователя или было выполнено программой (например, с помощью
setText или append).

WindowListener
Интерфейс WindowListener содержит семь методов:
public void windowOpened(WindowEvent event)
public void windowClosing(WindowEvent event)
public void windowClosed(WindowEvent event)
public void windowlconifled(WindowEvent event)
public void windowDeiconifled(WindowEvent event)
public void windowActivated(WindowEvent event)
public void windowDeactivated(WindowEvent event)
11.6. Низкоуровневая обработка событий 409

Если вам надо переопределить один или два метода, лучше всего воспользоваться
классом адаптера W i n d o w A d a p t e r , в котором реализованы "пустые" варианты ука­
занных выше методов. Метод windowOpened вызывается при первом открытии
окна, метод w i n d o w C l o s i n g — тогда, когда пользователь пытается закрыть окно, а
метод w i n d o w C l o s e d соответствует реальному закрытию окна. Методы w i n d o w -
I c o n i f i e d и w i n d o w D e i c o n i f i e d вызываются соответственно при минимизации
окна и восстановлении его размеров. Методы w i n d o w A c t i v a t e d и w i n d o w -
D e a c t i v a t e d соответствуют тем ситуациям, когда окно заслоняется другим ок­
ном, минимизируется либо деактивизируется любым другим способом. Метод
getWindow класса WindowEvent возвращает объект Window, определяюш^ий окно,
над которым выполняются действия.

11.6. Низкоуровневая обработка событий


Когда компонент получает событие, оно передается методу с именем process-
X x x E v e n t , где Ххх— это Mouse (т.е. метод называется p r o c e s s M o u s e E v e n t ) , Key,
F o c u s , A c t i o n и т.д. (Методы обработки перечислены в табл. 11.2.) Метод p r o c e s s -
X x x E v e n t передает событие каждому из объектов прослушивания, связанных с компо­
нентом. В большинстве случаев вы определяете обработчик и не заботитесь о том, ка­
кие действия выполняются "за сценой". Однако в некоторых случсшх приходится непо
средственно использовать методы р г о с е s s X x x E v e n t . Так, например, если вы хотите,
чтобы в ответ на любое событие, связанное с мышью, выполнялись одни и те же дейст­
вия, проще использовать метод p r o c e s s M o u s e E v e n t , чем переопределять все пять ме­
тодов, объявленных в интерфейсе M o u s e L i s t e n e r . Переопределяя метод p r o c e s s -
X x x E v e n t , почти всегда необходимо обращаться к методу s u p e r . p r o c e s s X x x E v e n t ,
поскольку именно он вызывает связанные обработчики. Если вы забудете вызвать
s u p e r . p r o c e s s X x x E v e n t и попытаетесь добавить к компоненту объекты прослуши­
вания, их методы не будут вызываться. Обычно суперкласс вызывается в конце метода
p r o c e s s X x x E v e n t , но если вы хотите, чтобы обработчики активизировались перед
выполнением вашего кода, вызов s u p e r . p r o c e s s X x x E v e n t необходимо включить в
начало метода.

Методика профессионалов

Переопределяя processMouseEvent, включайте в тело этого метода ., ^ Ш к


вызов super.processMouseEvent. Аналогично, переопределяя любой ^ШН81^
из методов processXxxEvent, включайте в него вызов super. process- ^НННВ
XxxEvent. ^йЩЯг

Для повышения эффективности система не передает событие методу p r o c e s s ­


X x x E v e n t , если вы явно не укажете, что такая передача должна быть выполнена. Сде­
лать это можно, вызывая метод е п а Ы е Е v e n t s и передавая ему маск)% соответствую­
щую типу события. Маски определены в классе AWTEvent. Например, для того чтобы
разрешить передачу методу p r o c e s s M o u s e E v e n t событий, соответствующих дейст­
виям с кнопкой мыши, надо использовать следующее выражение:

enableEvents(AWTEvent.MOUSE EVENT MASK);


410 Глава 1 1 . События, связанные с мышью и клавиатурой

Для событий, связанных с клавиатурой, это выражение выглядит так:


enableEvents (AWTEvent .KEY__EVENT__MASK) ;
Для того чтобы отслеживать фокус, вызов метода принимает следующий вид:
enableEvents (AWTEvent. FOCUS__EVENT_MASK) ;
Передачу различных событий можно разрешить с помощью одного вызова
e n a b l e E v e n t s . П р и этом надо выполнить над масками операцию поразрядного
И Л И . Например:
enableEvents(AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.KEY_EVENT_MASK |
AWTEvent.FOCUS_EVENT_MASK);
Методам p r о с е s s X x x E v e n t передаются параметры, типы которых определяются
как XxxEvent. Так, например, методу р г о с е s s M o u s e E v e n t передается параметр
MouseEvent, методу p r o c e s s K e y E v e n t — параметр KeyEvent, p r o c e s s F o c u s E v e n t ~
FocusEvent, p r o c e s s I t e m E v e n t — I t e m E v e n t , p r o c e s s A c t i o n E v e n t — A c t i o n E v e n t
и T. Д. В данном правиле существует одно исключение: для метода p r o c e s s M o u s e -
M o t i o n E v e n t не определен собственный тип параметра, вместо этого он, как и
p r o c e s s MouseEvent, использует параметр типа MouseEvent. Методу enableEvents со­
ответствует метод d i s a b l e E v e n t s ; он используется для отмены выполненных ранее
установок. Заметьте, что класс AWTEvent принадлежит пакету j a v a . awt, а MouseEvent
и другие типы событий — пакету j a v a . a w t . e v e n t . Таким образом, работая с события­
ми, необходимо импортировать оба пакета.
В листинге 11.8 приведен код аплета, который использует метод p r o c e s s M o u s e -
E v e n t для обработки событий, связанных с входом и выходом к)рсора из области ап­
лета, и с нажатием и отпусканием кнопок мыши. Метод p r o c e s s M o u s e M o t i o n E v e n t
используется для обработки событий,, связанных с перемещением мыши. Результаты
выполнения аплета показаны на рис. 11.3.

Листинг. 1 1 . 8 . M o u s e R e p o r t e r . J a v a

import java.applet.Applet;
import java.awt.*;
import Java.awt.event.*;

/ * * Вывод общих сведений о событиях, связанных с мышью.


* Вместо обычных о б р а б о т ч и к о в событий используются
* методы p r o c e s s X x x E v e n t .
V
p u b l i c c l a s s MouseReporter extends Applet {
public void i n i t O {
s e t B a c k g r o u n d ( C o l o r . b l u e ) ; / / Чтобы выделить а п л е т в окне
enableEvents(AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE MOTION EVENT MASK);

p u b l i c void processMouseEvent(MouseEvent event) {


S y s t e m . o u t . p r i n t l n ( " M o u s e e n t e r / e x i t or c l i c k a t (" +
11.6. Низкоуровневая обработка событий 411

event.getXO + "," +
event.getYO + ").");
// В случае использования обработчика MouseListener:
super.processMouseEvent(event);
}
public void processMouseMotionEvent(MouseEvent event) {
System.out.println("Mouse move/drag at (" +
event.getXO + ", " +
event.getYO + ").");
// В случае использования обработчика MouseMotionListener:
super.processMouseMotionEvent(event);
}

t-4-l'i''IHJ-l'lU4'rl.H.H'NIHH.Ht4'-l
•j^ -;; "3 -Ti ^- ,^ • j ^ rf '^ -M a
Mouse Event Reporter Mouse enter/exit or click at (7.0).
Mouse move/drag at (7,0).
Mouse move/drag at (9.2).
Mouse move/drag at (11.4).
Mouse move/drag at (17.6).
Mouse move/drag at (23,14).
Mouse move/drag at (29.16).
Mouse move/drag at (31,18).
Mouse move/drag at (32,20)
Mouse move/drag at (34.22)
Mouse move/drag at (36.23).
Mouse move/drag at (38,24)
Mouse move/drag at (38,25).
Mouse enter/exit or click at (38.25)
Mouse enter/exit or click at (38.25)
Mouse enter/exit or click at (38.25)

Ш _J^
;isg*;«^j>»r Appiet Mo«.isefi»{

Рис. 11.3. Результаты выполнения аплета MouseReporter после входа


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

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


в табл. 11.2.

Таблица 11.2. Методы низкоуровневой обработки событий

Метод обработки событий Тип события Маска для метода enableEvents


(определена как константа
в классе AWTEvent)
processАсtionEvent ActionEvent ACTION_EVENT_MASK
processAdjustmentEvent AdjustmentEvent ADJUSTMENT_EVENT_MASK
prосеssComponentEvent ComponentEvent COMPONENT_EVENT_MASK
processContainerEvent ContainerEvent CONTAINER_EVENT_MASK
prосеssFocusEvent FocusEvent FOCUS EVENT MASK
412 Глава 11. События, связанные с мышью и клавиатурой

Окончание табл. 11.2


processItemEvent ItemEvent ITEM_EVENT_MASK
processKeyEvent KeyEvent KEY_EVENT_MASK
prccessMouseEvent MouseEvent MOUSE_EVENT_MASK
prосеssMouseMotionEvent MouseEvent MOUSE_MOTION_EVENT_MASK
processTextEvent TextEvent TEXT__EVENT_MASK
processWindowEvent WindowEvent WINDOW EVENT MASK

11.7. Поле редактирования с проверкой


орфографии
в листинге 11.9 представлен код, предназначенный для поддержки поля редакти­
рования; в этом поле пользователь вводит название языка программирования. В дан­
ной программе отслеживаются три типа событий: события, связанные с клавиатурой
(пользователь вводит отображаемый символ в то время, когда поле редактирования
имеет фокус), события, связанные с фокусом (поле редактирования получает фок)т
ввода), и событие, заключающееся в том, что пользователь нажимает клавишу <Enter>
при наличии фокуса. Когда программа распознает отображаемый символ (для этого
используется метод k e y T y p e d класса K e y A d a p t e r ) , она сравнивает введенную строку
с началом строк из словаря языков программирования и заменяет ее наиболее подхо­
дящей подстрокой. В ответ на нажатие пользователем клавиши <Enter> (для распо­
знавания этого события используется метод a c t i o n P e г f o r m e d , объявленный в ин­
терфейсе A c t i o n L i s t e n e r ) текст дополняется до полного названия языка програм­
мирования, наиболее близкого к введенной строке. П р и получении полем редакти­
рования фок\'са ввода (метод f o c u s G a i n e d класса F o c u s A d a p t e r ) в нем подсвечива­
ется подсказка. В листинге 11.10 содержится код аплета, использующего данное поле
редактирования, а на рис. 11.4 показан результат его выполнения.

Листинг 1 1 . 9 . LanguageField.Java

import java.awt.^;
import Java.awt.event.*;
/ * * Компонент T e x t F i e l d , предназначенный для ввода н а з в а н и й
* языков программирования и к о р р е к т и р о в а н и я в в е д е н н о г о
* текста.
V
public class LanguageField extends TextField {
private String[] substrings =
{ "", "J", "Ja", "Jav", "Java" };

public LanguageField() {
addKeyListener(new SpellingCorrector ());
addActionListener(new WordCompleter());
addFocusListener(new SubliminalAdvertiser());
11.7. Поле редактирования с проверкой орфографии 413

// Перевод курсора в конец текстового поля.

private void setCaretO {


setCaretPosition(5);
}
// Обработчик, корректирующий введенные символы.

private class SpellingCorrector extends KeyAdapter {


public void keyTyped(KeyEvent event) {
setLanguage();
setCaret();
}
// Ввод части имени языка программирования, наиболее
// полно соответствующего последовательности символов,
// заданных пользователем.
private void setLanguage() {
int length = getText().length();
if (length <= 4) {
setText(substrings[length]);
} else {
setText("Java");
}
setCaret();
}

// Обработчик, заменяющий часть имени полным названием


// языка программирования.

private class WordCompleter implements ActionListener {

// При нажатии клавиши <Return> поле заполняется данными.


public void actionPerformed(ActionEvent event) {
setText("Java");
setCaret();
}
}
// Обработчик, предлагающий пользователю подсказку.
private class SubliminalAdvertiser extends FocusAdapter {
public void focusGained(FocusEvent event) {
String text = getText();
for(int i=0; i<10; i++) {
setText("Hint: Java");
setText(text);
414 Глава 1 1 . События, связанные с мышью и клавиатурой

Листинг 11.10. JavaTextField. Java

import Java.applet.Applet;
import java.awt.*;

/'^* Пользователь может ввести название <В>любого</В>


* языка программирования.
Ч
public class JavaTextField extends Applet {
public void initO {
setFont(new Font("Serif", Font.BOLD, 14));
setLayout(new GridLayout(2, 1));
add(new Label("Enter a Good Programming Language",
Label.CENTER));
LanguageField langField = new LanguageField();
Font langFont = new Font("SansSerif", Font.BOLD, IE
langField.setFont(langFont);
add(langField);
}
}

H,ti№HiflTlil'l'H'Hi'ii№k№a, рт1^Ш1-1пЩ|

d
Choose a Language, Any Language

J:
i^HyCompulef

Рис. 11.4. JavaTextField после ввода поль­


зователем "C#"

11.8. "Чертежная доска"


В листинге 11.11 представлен аплет, который позволяет пользователю рисовать
произвольные фигуры. Программа запоминает координаты х и у той точки, в которой
пользователь нажал кнопку мыши, и затем, по мере передвижения курсора при нажа­
той кнопке, она рисует отрезки линий между предыдущей и текущей точками. Так, на
рис. 11.5 показан план исследовательского центра, нарисованный одним из авторов
книги, и обозначено место, где он оставлял свою машину.
11.8. "Чертежная доска" 415

Листинг 11.11.SimpleWhiteboard.Java

import Java.applet.Applet;
import java.awt.*;
import Java.awt.event.*;

/** Аплет, позволяющий рисовать произвольные фигуры. */

public class SimpleWhiteboard extends Applet {


protected int lastX=0, lastY=0;

public void initO {


setBackground(Color.white) ;
setForeground(Color.blue);
addMouseListener(new PositionRecorder());
addMouseMotionListener(new LineDrawer());

protected void record(int x, int y) {


lastX = x;
lastY = y;
}
// Запись позиции, в которой курсор мыши появился в окне
// или в которой пользователь нажал кнопку.

private class PositionRecorder extends MouseAdapter {


public void mouseEntered(MouseEvent event) {
requestFocus();
record(event.getX(), event.getY());
}
public void mousePressed(MouseEvent event) {
record(event.getX(), event.getY());
}
}
// При перетаскивании мыши последовательные позиции
// курсора соединяются короткими отрезками.

private class LineDrawer extends MouseMotionAdapter {


public void mouseDragged(MouseEvent event) {
int X = event.getX0;
int у = event.getY0;
Graphics g = getOraphics();
g.drawLine(lastX, lastY, x, y) ;
record(x, y ) ;
}
}
416 Глава 1 1 . События, связанные с мышью и клавиатурой

1Щ- SifMite Whiteboard Applet - Netscape "^ "^ Н{я!Е31

^' / 3 :0 .:^. Й ^1 1^^' 1 1' SI


Simple Whiteboard Applet

X о
[ID

^_ ^,. . ^ ........ J
i # = ^ "' Ai^JtetS^rfeWhfteboafdrynntnQ ; >,^ У^ J ^ Q ^/^

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


вания курсора мыши

Модифицированный вариант '^чертежной доски"


Несмотря на высокий художественный уровень картины, показанной на рис. 11.5,
некоторые читатели, возможно, еще не способны воспринимать настоящие произведе­
ния искусства, поэтому рисунок нуждается в надписях. Модифицированный вариант ап-
лета (листинг 11.12) позволяет дополнить графическое изображение текстом. При на­
жатии клавиши метод keyTyped преобразует введенное значение (в данном случае
i n t ) в строку, выводит строку в текущей позиции и смещает текущую позицию вправо, в
зависимости от размеров строки. Результаты выполнения аплета показаны на рис. 11.6.

Листинг 11.12. Whiteboard.Java

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
import J a v a . a w t . e v e n t . * ;
/ • • Модифицированная ''чертежная доска" позволяет
* дополнять изображение текстом.

p u b l i c c l a s s Whiteboard extends SimpleWhiteboard


p r o t e c t e d FontMetrics fm;
p u b l i c void i n i t O {
super.init();
Font font = new F o n t ( " S e r i f " , Font.BOLD, 2 0 ) ;
setFont(font);
fm = g e t F o n t M e t r i c s ( f o n t ) ;
addKeyListener(new CharDrawer());
11-9. Резюме 417

private class CharDrawer extends KeyAdapter {


// Символ, введенный пользователем, отображается,
// после чего позиция сдвигается вправо.
public void keyTyped(KeyEvent event) {
String s = String.valueOf(event.getKeyChar0);
getGraphics().drawstring(s, lastX, lastY);
record(lastX + fm.stringWidth(s), lastY);
}

^WMeboarid Applet - Nelscape


Ede £<Й Ytevv ^o £w!(Ttum:at.O( М Ф

1 :^ Э ^1 j^. Ы ~s ё: ;3 13 Д:

Whiteboard Applet

1 Joluis Hopldits Road

Route
Park
29 Here О - Pond

- K-Ceiitei
• -•

53|-\==ф. A { 4 ^ V4)it«bu«d {unnng

Рис. 11.6. Модифицированный вариант аплета: при вводе


символов с клавиатуры текст отображается на экране

11.9. Резюме
Аплет, созданный в последнем разделе данной главы, не так уж плох, если принять
во внимание размеры исходного кода. Однако в нем недостает многих важных
средств. Пользователь не может выбирать цвет и шрифт, задавать тип операций
(рисование отрезков прямых, окружностей, прямоугольников и др.). Кроме того,
изображение, нарисованное пользователем, недолговечно. Поскольку рисование
осуществляется непосредственно в окне, то как только область, занимаемая аплетом,
будет закрыта на время другим окном, изображение безвозвратно исчезнет. В главе 16
мы обсудим некоторые подходы, позволяющие разрешить эту проблему. Наиболее
часто используется двойная буферизация — технология, позволяющая рисовать изо­
бражения вне экрана. В главе 13, посвященной компонентам AWT, мы рассмотрим
списки, раскрывающиеся меню и другие элементы графического пользовательского
интерфейса. Наконец, созданный аплет должен быть доступен большому числу поль­
зователей. Способы создания серверов будут обсуждаться в главе 17.
ДИСПЕТЧЕРЫ
КОМПОНОВКИ

В ЭТОЙ главе...

Применение диспетчеров компоновки при разработке


интерфейса.
Диспетчер компоновки FlowLayout.
Диспетчер компоновки BorderLayout.

Диспетчер компоновки GridLayout.

Диспетчер компоновки CardLayout.

Диспетчер компоновки GridBagLayout.

Диспетчер компоновки BoxLayout.

Размещение компонентов вручную.


Эффективное использование диспетчеров компоновки.
S1/\ZJ^:EJ

ри создании объекта C o n t a i n e r с ним автоматически связывается вспомога­

П тельный объект, который называется диспетчером компоновки (layout manager).


Этот объект управляет размерами и размещением компонентов внутри кон­
тейнера. Диспетчер компоновки избавляет разработчика от необходимости решать
задачу вычисления координат объектов. Заметьте, что эта задача усложняется тем,
что на различных платформах размеры компонентов могут различаться, а пользова­
тели в процессе работы часто изменяют размеры окна. Кроме того, при разработке
Web-страницы ее внешний вид может неоднократно меняться.
Диспетчер компоновки, автоматически связываемый с контейнером, подходит
для выполнения простейших операций размещения элементов, но он не обеспечива­
ет достаточной гибкости при решении реальных задач. Однако, используя вложен­
ные контейнеры, с каждым из которых связан свой диспетчер компоновки, можно
разместить элементы практически любым способом. Кроме того, вы всегда можете
отключить диспетчер компоновки и явным образом указать расположение управ­
ляющих элементов. Java также позволяет реализовать новый диспетчер компоновки,
ориентированный на решение конкретных задач.
При использовании диспетчера компоновки разработчик сначала связывает его с
контейнером, вызывая для этого метод s e t L a y o u t (либо принимая диспетчер, пред­
лагаемый по умолчанию), а затем с помощью метода add добавляет к контейнеру ком­
поненты. В аплете связывание диспетчера компоновки с контейнером обычно осуще­
ствляется в методе i n i t . В приложении диспетчер компоновки часто устанавливает­
ся в теле конструктора.
В этой главе мы рассмотрим диспетчеры компоновки AWT: FlowLayout,
BorderLayout, GridLayout, CardLayout и GridBagLayout, a затем диспетчер
BoxLayout, принадлежащий Swing. Мы также обсудим вопросы эффективного ис­
пользования диспетчера компоновки и способы размещения элементов при отсутст­
вии диспетчера.
420 Глава 12. Диспетчеры компоновки

1 2 . 1 . Диспетчер компоновки FlowLayout


Диспетчер компоновки F l o w L a y o u t приводит компоненты к естественным раз­
мерам, а затем размещает их в виде строк в окне. Первый элемент помещается в верх­
ней строке, следующий элемент располагается за ним и т.д. до тех пор, пока элементы
помещаются в текущей строке. После того как строка будет заполнена, процедура по­
вторяется для следующей строки. По умолчанию интервалы между компонентами в
строке и между строками устанавливаются равными пяти пикселям, а элементы вы­
равниваются по центру строки. Конструктор F l o w L a y o u t , который будет описан ни­
же, позволяет также задавать выравнивание элементов по левому и правому краям.
F l o w L a y o u t используется по умолчанию контейнерами P a n e l , J P a n e l и A p p l e t .
В листинге 12.1 представлен код аплета, в котором размещаются пять кнопок. Ши­
рина окна такова, что в одной строке могут поместиться лишь четыре кнопки. Как пока­
зано на рис. 12.1, первые четыре кнопки располагаются в верхней строке, а последняя
кнопка— в следующей строке, выровненная по центру. Поскольку F l o w L a y o u t приме­
няется объектами A p p l e t по умолчанию, нет необходимости вызывать метод
s e t L a y o u t . Для контейнеров, использующих другие диспетчеры компоновки, вам при­
дется включить в программу выражение s e t L a y o u t (new F l o w L a y o u t () ), с помощью
которого диспетчер F l o w L a y o u t связывается с контейнером.

Листинг 1 2 . 1 . FlowTGSt.Java

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
/** Диспетчер компоновки FlowLayout размещает компоненты
по строкам.
V
p u b l i c c l a s s FlowTest extends Applet {
p u b l i c void i n i t O {
f o r ( i n t i = l ; i<6; i++) {
add(new Button ("Button " -f- i) ) ;
}
}
}

Ap0et

Button 1 I Button 2 1 Buttons I Button 4 I Рис. 12.1. Диспетчер компоновки FlowLayout


располагает компоненты по строкам. Если
компонент не помещается в текущей строке,
он размещается в следующей
1 2 . 1 . Диспетчер компоновки FlowLayout 421

Конструкторы FlowLayout
public FlowLayoutO
Данный конструктор создает диспетчер компоновки F l o w L a y o u t , который раз­
мещает элементы по центру строки, оставляя между элементами в строке и между
строками интервал, равный пяти пикселям.

public FlowLayout(int alignment)


Данный конструктор создает диспетчер компоновки F l o w L a y o u t с выравнивани­
ем по левому, правому краям или по центру строки. Для указания типа выравнива­
ния используются значения параметра F l o w L a y o u t .LEFT, F l o w L a y o u t . RIGHT и
F l o w L a y o u t .CENTER. Интервал между элементами в строке и между строками ра­
вен пяти пикселям.

public FlowLayout(int a l i g n m e n t , int hGap, int vGap)


Этот конструктор создает диспетчер F l o w L a y o u t с указанным типом выравнива­
ния. Параметр hGap задает интервал между элементами в строке, а параметр
vGap — интервал между строками.

Методы FlowLayout
Диспетчер компоновки F l o w L a y o u t содержит также след)^ющие методы.

public int getAlignmentO


public void setAlignment(int alignment)
Данные методы определяют текущий тип выравнивания и изменяют его. Как и в
конструкторе, тип выравнивания задается с помощью констант F l o w L a y o u t .LEFT,
FlowLayout.RIGHT и FlowLayout.CENTER.

public int getHgapO


public void setHgap(int hGap)
Данные методы определяют и изменяют интервал между элементами в строке.
Значение по умолчанию равно 5.

public int getVgapO


p u b l i c void setVgap(int vGap)
Данные методы определяют и изменяют интервал между строками. Значение по
умолчанию равно 5.
Экземпляр F l o w L a y o u t , используемый по умолчанию объектами P a n e l , хранится
в переменной класса ( s t a t i c ) . Выполняя действия с диспетчерами компоновки, надо
соблюдать осторожность, чтобы избежать нежелательных побочных эффектов. Не­
смотря на то что переменная, в которой хранится совместно используемый объект
F l o w L a y o u t , объявлена как f i n a l , это совсем не означает, что объект не может быть
изменен. Постоянной остается лишь ссылка на объект, которая не может указывать
422 Глава 12. Диспетчеры компоновки

на другой экземпляр класса. Поля объекта, на которые ссылается переменная f i n a l ,


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

P a n e l р = new P a n e l ( ) ;
p.setLayout(new FlowLayout(FlowLayout.CENTER,10,5));

Последовательность команд, приведенная ниже, для этой цели не подходит.

Panel р = new Panel();


FlowLayout layout = (FlowLayout)p.getLayout();
layout.setHgap(10);

Ha первый взгляд может показаться, что второй способ более эффективен, по­
скольку при этом не создается новый экземпляр F l o w L a y o u t . Однако следует учиты­
вать, что изменения, выполненные таким способом, будут воздействовать и на другие
панели. Для каждой из панелей, использующей диспетчер компоновки F l o w L a y o u t ,
расстояние между элементами будет равно 10.

12.2. Диспетчер компоновки BorderLayout


Диспетчер компоновки B o r d e r L a y o u t делит окно на пять областей с именами
NORTH, SOUTH, EAST, WEST и CENTER. Для добавления компонента к области использу­
ется вариант метода a d d , которому передаются два параметра. Первый параметр оп
ределяет компонент, а второй представляет собой строк), содержащую имя области,
в которой должен быть размещен указанный компонент.

a d d ( b u t t o n F o r T o p , BorderLayout.NORTH);
a d d ( s c r o l l b a r F o r R i g h t S i d e , BorderLayout.EAST);
a d d ( p a n e l F o r R e m a i n i n g S p a c e , BorderLayout.CENTER);

Диспетчер компоновки управляет размерами и размещением компонентов. Разме­


ры компонентов, которые помещаются в областях NORTH и SOUTH, изменяются сле­
дующим образом: высота устанавливается равной обычной высоте данного компо­
нента, а ширина изменяется так, чтобы она совпадала с шириной окна. Аналогично,
ширина компонентов, помещаемых в области EAST и WEST, принимается равной
обычной ширине компонента, а высота устанавливается в соответствии с высотой
окна, за вычетом размеров компонентов, помещенных в областях NORTH и SOUTH.
Компонент, размещаемый в области CENTER, растягивается так, чтобы его размеры
совпадали с размерами области. Конструктор B o r d e r L a y o u t позволяет задавать рас­
стояния между областями, которые по умолчанию принимаются равными 0. Диспет­
чер компоновки B o r d e r L a y o u t используется с контейнерами Frame, D i a l o g и
Window, а также с панелями содержимого J A p p l e t , J F r a m e , J D i a l o g и JWindow.
В листинге 12.2 показан аплет, с которым связан диспетчер компоновки
B o r d e r L a y o u t . В каждой из областей, определяемых диспетчером, размещается
кнопка. Заметьте, что, в отличие от F l o w L a y o u t , размеры кнопок изменяются в со­
ответствии с размерами окна.
1 2 . 2 . Д и с п е т ч е р компоновки BorderLayout 423

Листинг 12.2. BoiTderTest. Java

import Java, appl^it. Applet;


import j ava. awt. '•';

/** Пример использования BorderLayout. Ч

public class BorderTest extends Applet {


public void initO {
setLayout(new BorderLayout());
add(new Button("Button 1"), BorderLayout.NORTH);
add(new Button("Button 2"), BorderLayout.SOUTH);
add(new Button("Button 3"), BorderLayout.EAST);
add(new Button("Button 4"), BorderLayout.WEST);
add(new Button("Button ), BorderLayout.CENTER);
}
}

^l4VI-)Hiyii;Hfr!l'!1tfPffiffMllirlgRl

B«)toft4 a«l«i$ аув${)з|


Рис. 12.2. Диспетчер компоновки
' 8«lton5 1 BorderLayout делит окно на пять
Applet started
областей

При работе с BorderLayout начинающие программисты часто допускают ошибку.


Вместо выражения
add(component,BorderLayout.REGION);
они используют более привычный вызов
add(component);
Если вы явно не укажете область, компонент по умолчанию будет помещен в об­
ласть CENTER и займет все доступное пространство. В результате на экране будет ото­
бражаться лишь верхний компонент (включенный последним). При компиляции та­
кой программы предупреждающие сообщения не выводятся.
Внимание!

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


этот компонент по умолчанию будет размещен в области CENTER

Обратите также внимание на то, что при работе с BorderLayout не следует вклю­
чать в область больше одного элемента. В противном случае новый элемент будет
размещен поверх включенного ранее. Если вам надо добавить в область несколько
элементов, разместите их в новом контейнере Panel и включите панель в требуемую
область.
424 Глава 1 2 . Д и с п е т ч е р ы компоновки

М е т о д и к а профессионалов

Не включайте в область больше одного компонента. Если необходи- ^


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

Конструкторы BorderLayout
public BorderLayoutO
Данный конструктор создает объект B o r d e r L a y o u t , области которого непосред­
ственно граничат друг с другом.

public BorderLayout(int h G a p , int vGap)


Данный конструктор создает объект B o r d e r L a y o u t , в котором интервал между
областями WEST и CENTER, а также между CENTER и EAST равен hGap. Аналогично,
интервал между NORTH и CENTER, а также между CENTER и SOUTH равен vGap.

Методы BorderLayout
Работая с контейнерами Frame и D i a l o g , в которых по умолчанию используется
рассматриваемый здесь диспетчер компоновки, не следует вызывать методы s e t H g a p
и s e t V g a p объекта, возвращаемого методом g e t L a y o u t . Подобные установки воз­
действуют на все контейнеры, использующие по умолчанию B o r d e r L a y o u t . Подоб­
ные рекомендации справедливы и для панелей содержимого объектов J F r a m e ,
JApplet,JWindow и JDialog.

public int getHgapO


public void setHgap(int hGap)
Данные методы определяют и устанавливают горизонтальный интервал между об­
ластями.

public int getVgapO


public void setVgap(int vGap)
Данные методы определяют и устанавливают вертикальный интервал между об­
ластями.

public float getLayoutAlignmentX(Container с)


public float getLayoutAlignmentY(Container с)
Данные методы определяют тип выравнивания для контейнера. Подробно этот
вопрос будет рассмотрен в главе 13.
12.3. Диспетчер компоновки GridLayout 425

12.3. Диспетчер компоновки GridLayout


Диспетчер G r i d L a y o u t делит окно на одинаковые прямоугольные области, или
ячейки. Размер ячеек зависит от числа строк и столбцов, формируемых в окне. Пере­
бор ячеек осуществляется построчно слева направо; в таком порядке размещаются
компоненты, добавляемые к контейнеру. Размеры компонентов изменяются так, что­
бы они совпадали с размерами ячеек. Конструктор G r i d L a y o u t позволяет задавать
размеры пустого пространства между строками и столбцами.
В листинге 12.3 приведен код аплета. Область аплета делится на две строки и три
столбца; в каждой из ячеек размещается кнопка. Результаты выполнения аплета пока­
заны на рис. 12.3.

Листинг 12.3. G r i d T e s t . J a v a

i m p o r t J a v a , a p p l e t .Applets-
import ja'va.awt.*;

/ * * Пример и с п о л ь з о в а н и я G r i d L a y o u t . */

public c l a s s GridTest extends Applet {


public void i n i t O {
s e t L a y o u t ( n e w G r i d L a y o u t ( 2 , 3 ) ) ; / / 2 rows, 3 c o l s
add(new B u t t o n ( " B u t t o n O n e " ) ) ;
add(new B u t t o n ( " B u t t o n T w o " ) ) ;
add(new B u t t o n ( " B u t t o n T h r e e " ) ) <
add(new B u t t o n ( " B u t t o n F o u r " ) ) ;
add(new B u t t o n ( " B u t t o n F i v e " ) ) ;
add(new B u t t o n ( " B u t t o n S i x " ) ) ;
}

i-Haii,!gy>"-'-'»H
Applet

Buttor» One 8<Jttftn Two Button Three 1

Button Four Button Five Bunof) Stx 1 Рис. 12.3. Диспетчер компоновки
G r i d L a y o u t делит окно на прямоугольные
Applet started.
области одинакового размера

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

public GridLayout(int rows, int cols)


Данный конструктор создает диспетчер компоновки G r i d L a y o u t , который делит
окно на указанное число строк и столбцов. Каждая из ячеек выравнивается отно-
426 Глава 12. Диспетчеры компоновки

сительно своих соседей. Параметр rows или c o l s (но не оба одновременно) мо­
жет быть равен 0. Если параметр rows равен О, исполняющая система Java старает­
ся разделить окно на строки так, чтобы в каждом из столбцов находилось равное
(или приблизительно равное) число элементов. Аналогично, если параметр c o l s
равен О, система старается разделить окно на столбцы так, чтобы в каждой строке
находилось приблизительно равное число элементов. Данный подход иллюстри­
рует код приложения, приведенный в листинге 12.4. В окне, как показано на
рис. 12.4, размещаются 11 кнопок, причем при создании диспетчера компоновки
указано нулевое число столбцов, а количество строк задается в командной строке.
Класс W i n d o w U t i l i t i e s . Java определен в листинге 14.1.

Листинг 1 2 . 4 . E l e v e n B u t t o n s . J a v a

import java.awt.*;
import javax.swing.*;

/** Данный код д е м о н с т р и р у е т отображение компонентов в с л у ч а е ,


* если для д и с п е т ч е р а компоновки з а д а н о н у л е в о е число с т о л б ц о в .
* Число с т р о к у к а з ы в а е т с я в командной с т р о к е (по умолчанию
* принимается з н а ч е н и е 2), а число с т о л б ц о в выбирается т а к ,
* чтобы в каждой с т р о к е находилось п р и б л и з и т е л ь н о одинаковое
* число компонентов.

public class ElevenButtons extends JPanel {


public ElevenButtons(int numRows) {
setLayout(new GridLayout(numRows, 0));
for(int i=0; i<ll; i++) {
add(new JButton("Button " + i));
}
}
public static void main(String[] args) {
int numRows = 2;
if (args.length > 0) {
numRows = Integer.parseint(args[0]);
}
String title = "11 Buttons using GridLayout(" +
numRows + " , 0 ) . " ;
WindowUtilities.setNativeLookAndFeel();
WindowUtilities.openlnJFrame(new ElevenButtons(numRows),
550, 200, title);
12.4. Диспетчер компоновки CardLayout 427

1 ^ 1 1 Button» 1jsing G riciL«wout(2>0l.


ws-'^mmmi
If 1
1 1
1 Sutton 0 1 Stittonl Bvm\2 Button 3 &ulton4 BtittonS 1
1

1 •
SunonS Sutton r e^ttons ' Buttond . SuttonIO

Рис. 12.4. Если при вызове конструктора диспетчера


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

public GridLayout(int rows, int c o l s , int hGap, int vGap)


Данный конструктор делит окно на указанное число строк и столбцов, оставляя
между строками и столбцами число пикселей, определяемое соответственно пара­
метрами hGap и vGap. Один из параметров r o w s или c o l s может быть равен 0.
Значение О интерпретируется как "любое количество" и обрабатывается так же,
как и для предыдущего конструктора.

Методы GridLayout
public int getRowsO
p u b l i c void setRows(int rows)
Данные методы определяют и изменяют число строк.

public int getColumns()


p u b l i c void s e t C o l u m n s ( i n t cols)
Данные методы определяют и изменяют число столбцов.

p u b l i c int getHgapO
p u b l i c void setHgap(int hGap)
Данные методы определяют и изменяют интервал между столбцами.

p u b l i c int getVgapO
public void setVgap(int vGap)
Данные методы определяют и изменяют интервал между строками.

12.4. Диспетчер компоновки CardLayout


Диспетчер компоновки C a r d L a y o u t размещает компоненты (которые называют
также картами) один поверх другого так, что в каждый момент времени отображается
только один из них. Для использования данного диспетчера надо создать экземпляр
C a r d L a y o u t , связать его с контейнером и обязательно сохранить ссылку на создан­
ный объект.
428 Глава 12. Диспетчеры компоновки

Panel cardPanel;
CardLayout layout;

layout = new CardLayout{);


cardPanel.setLayout(layout);
Ссылка на диспетчер компоновки понадобится впоследствии. П р и включении
компонента в окно с ним, как показано ниже, связывается имя.
cardPanel.add(component1, "Card 1");
cardPanel.add(component2, "Card 2");

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


Существует несколько способов, позволяющих сообщить диспетчеру компоновки о том,
что конкретный элемент должен отображаться в окне. Вы можете вызвать метод show,
предав ему в качестве параметра имя элемента, либо обратиться к методам f i r s t , l a s t ,
p r e v i o u s и n e x t . В любом сл)^ае метод)^ в качестве параметра должен передаваться
контейнер, использующих данный диспетчер компоновки, например:
layout.show(cardPanel, "Card 1");
layout.first(cardPanel);
layout.next(cardPanel);
В качестве примера рассмотрим код аплета, представленный в листинге 12.5. Ап-
лет помещает объект Panel, использующий CardLayout, в правую часть окна, а на­
бор кнопок— в левую. В контейнер Panel включаются четыре объекта CardPanel
(листинг 12.6), каждый из которых, в свою очередь, содержит объекты Label и
ImageLabel (исходный код ImageLabel. Java вы можете найти по адресу
http://www.corewebprogramming.com/). Объект ImageLabel отображает одну из
игральных карт. Изображение на экране зависит от того, какая из кнопок активизи­
рована. Одна из четырех возможных конфигураций показана на рис. 12.5.

Листинг 12.5.CardDemo.Java

import Java.applet.Applet;
import java.awt.*;
import Java.awt.event.*;

/^* Пример использования диспетчера компоновки CardLayout.


* В правой части окна содержится компонент Panel, который
* использует CardLayout для управления четырьмя контейнерами
* (каждый из них представляет собой объект CardPanel, в
* котором отображается игральная карта). Кнопки в левой части
* вызывают методы, изменяющие состояние правой части окна.
Ч
public class CardDemo extends Applet implements ActionListener {
private Button first, last, previous, next;
private String[] cardLabels = { "Jack","Queen","King","Ace" };
private CardPanel[] cardPanels = new CardPanel[4];
private CardLayout layout;
private Panel cardDisplayPanel;
12.4. Диспетчер компоновки CardLayout

public void initO {


setBackground(Color.white);
setLayout(new BorderLayout());
addButtonPanel();
addCardDisplayPanel();
}

private void addButtonPanel() {


Panel buttonPanel = new PanelO;
buttonPanel,setLayout(new GridLayout(9^ 1));
Font buttonFont = new Font("SansSerif", Font.BOLD, 18);
buttonPanel.setFont(buttonFont);
for(int i=0; i<cardLabels.length; i++) {
Button button = new Button(cardLabels[i]);
button.addActionListener(this);
buttonPanel.add(button);
}
first = new ButtonC'First") ;
first.addActionListener(this);
last = new Button("Last");
last.addActionListener(this);
previous = new Button("Previous");
previous.addActionListener(this);
next = new Button("Next");
next.addActionListener(this);
buttonPanel. add (new Label (" ", Label. CENTER) )
buttonPanel.add(first);
buttonPanel.add(last);
buttonPanel.add(previous);
buttonPanel. add (next) .;
add(buttonPanel, BorderLayout.WEST);
}
private void addCardDisplayPanel() {
cardDisplayPanel = new PanelO;
layout = new CardLayout();
cardDisplayPanel.setLayout(layout);
String cardName;
for(int i=0; i<cardLabels.length; i++) {
cardName = cardLabels[i];
cardPanels[i] =
new CardPanel(cardName, getCodeBase(),
"images/" + cardName + ".gif");
cardDisplayPanel.add(cardPanels[i], cardName);
}
add(cardDisplayPanel, BorderLayout.CENTER);
}
public void actionPerformed(ActionEvent event) {
Button source = (Button)event.getSource();
if (source == first)
layout.first(cardDisplayPanel);
else if (source == last)
layout.last(cardDisplayPanel);
else if (source == previous)
layout.previous(cardDisplayPanel);
else if (source == next)
430 Глава 12. Диспетчеры компоновки

layout.next(cardDisplayPanel);
else
layout.show(cardDisplayPanel, source.getLeUDel0);
return;
}
}

Листинг 12.6.CardPanel.Java

import java.awt.*;
import java.net.*;

/** Панель, отображающая карту. Данный контейнер


* <В>не</В> использует CardLayout. Экземпляры CardPanel
* сами включаются в другое окно^ с которым связан
* диспетчер компоновки CardLayout.
V
public class CardPanel extends Panel {
private Label name;
private ImageLabel picture;

public CardPanel(String cardName,


URL directory, String imageFile) {
setLayout(new BorderLayout());
name = new Label(cardName, Label.CENTER);
name.setFont(new Font("Sanserif", Font.BOLD, 50));
add(name, BorderLayout.NORTH);
picture = new ImageLabel(directory, imageFile);
Panel picturePanel = new Panel ();
picturePanel.add(picture);
add(picturePanel, BorderLayout.CENTER);
setSize(getPreferredSize());
}
public Label getLabelO {
return(name);
}
public ImageLabel getlmageLabel () {
return(picture);
}
}
12.4. Диспетчер компоновки CardLayout 431

\ШЕшшшшшшшшшяшшштшшт
Applet , , ; ,

__^ Jack
yiie^si 1 1
'1
King

AC€
E

first III б^НбИОВ^^Вы и1

Last

Pr«vi«ii$
Г
Nesd; \ Ljji

Applet started.

Рис. 12.5. Диспетчер компоновки CardLayout распо­


лагает компоненты один над другим

Конструкторы CardLayout
public CardLayoutO
Данный конструктор создает экземпляр CardLayout, который располагает ком­
поненты в верхнем левом углу окна.

public CardLayout(int sideMargins, int topMargins)


Этот конструктор создает объект CardLayout, который резервирует указанное
число пикселей в левой и верхней частях окна.

Методы CardLayout
Класс CardLayout содержит перечисленные ниже методы. Заметьте, что пара­
метр C o n t a i n e r указывает на окно, использующее CardLayout, а не на компонент.

public void show(Container с, String cardName)


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

public void first(Container с)


public void last(Container с)
Эти методы отображают соответственно первый и последний из компонентов,
добавленных к контейнеру.
432 Глава 12. Диспетчеры компоновки

public void previous(Container с)


public void next(Container с)
Данные методы отображают соответственно предыдущий и следующий компо­
ненты. Если вы не используете специальный вариант метода a d d , который позво­
ляет явно указывать положение компонента в последовательности, элементы рас­
полагаются в том порядке, в каком они были включены в контейнер.

public int getHgapO


public void setHgap(int hGap)
Данные методы определяют и изменяют размеры пустого пространства, зарезер­
вированные в левой и правой частях окна. В данном случае лучше подошли бы
имена методов g e t S i d e M a r g i n s и s e t S i d e M a r g i n s , но методы были названы
так, чтобы обеспечить совместимость с другими диспетчерами компоновки.

public int getVgapO


public void setVgap(int vGap)
Данные методы определяют и изменяют размеры пустого пространства, зарезер­
вированные в верхней и нижней частях окна. Этим методам лучше подошли бы
имена g e t T o p M a r g i n s и s e t T o p M a r g i n s , но методы были названы так, чтобы
обеспечить совместимость с другими диспетчерами компоновки.

public float getLayoutAlignmentX(Container с)


public float getLayoutAlignmentY(Container с)
Данные методы указывают тип выравнивания для контейнера. Подробно этот во­
прос будет рассмотрен в разделе 13.2.

12.5. Диспетчер компоновки GridBagLayout


Диспетчер компоновки G r i d B a g L a y o u t обеспечивает гораздо большую гибкость
по сравнению с рассмотренными диспетчерами компоновки. К сожалению,
G r i d B a g L a y o u t во много раз сложнее в использовании. При использовании данного
диспетчера вы разбиваете окно на отдельные ячейки, а затем указываете, какие из
них должен занимать каждый компонент. Вы также определяете изменение размеров
компонентов и выравнивание внутри ячеек.
Основные деР1Ствия, выполняемые при работе с G r i d B a g L a y o u t , описаны ниже.

Создание экземпляра д и с п е т ч е р а компоновки и сохранение ссылки на н е г о

G r i d B a g L a y o u t l a y o u t = new G r i d B a g L a y o u t ( ) ;
setLayout(layout) ;

Создание объекта ограничений G r i d B a g C o n s t r a i n t s

GridBagConstraints c o n s t r a i n t s =
new G r i d B a g C o n s t r a i n t s ( ) ;
12.5. Диспетчер компоновки GridBagLayout 433

Установка параметров G r i d B a g C o n s t r a i n t s для первого компонента


constraints.gridx = xl;
constraints.gridy = yl;
constraints.gridwidth = widthl;
constraints.gridheight = heightl;

Включение в окно первого компонента с учетом ограничений


add(component1, c o n s t r a i n t s ) ;

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

Объект GridBagConstraints
Помимо диспетчера компоновки, связываемого с контейнером, необходимо соз-
ать объект G r i d B a g C o n s t r a i n t s , который определяет ограничения при размеще-
ии каждого компонента. При включении компонента в контейнер метод)^ add также
ередается объект G r i d B a g C o n s t r a i n t s . После завершения работы метода add со-
гояние объекта G r i d B a g C o n s t r a i n t s может быть изменено, поэтому создавать от­
ельный объект такого типа для каждого элемента Component нет необходимости. В
Tiacce G r i d B a g C o n s t r a i n t s определено большое количество полей; значения мно-
лх из них должны быть установлены для каждого компонента. Поля GridBagConst-
a i n t s описаны ниже.

public int gridx


public int gridy
Данные переменные определяют верхний левый угол компонента.

public int gridwidth


public int gridheight
Эти переменные указывают число столбцов и строк, занимаемых компонентом.
Заметьте, что вам не надо указывать общее число строк или столбцов в диспетчере
компоновки; GridBagLayout определяет эти значения автоматически. Вы также
можете присвоить g r i d w i d t h и g r i d h e i g h t значение G r i d B a g C o n s t r a i n t s .
RELATIVE, а затем добавлять компоненты, предоставив диспетчеру компоновки
вычислять значения ширины и высоты. Следуя этому подходу, надо использовать
G r i d B a g C o n s t r a i n t s . REMAINDER для последнего значения в строке или столб­
це. По умолчанию используется величина, равная 1.

public int anchor


Если значение f i l l равно G r i d B a g C o n s t r a i n t s .NONE, поле anchor задает рас­
положение элемента. Для того чтобы разместить элемент в центре, используйте
GridBagConstraints.CENTER. Константы GridBagConstraints.NORTH, G r i d ­
B a g C o n s t r a i n t s .NORTHEAST, GridBagConstraints.EAST, GridBagConstra­
i n t s .SOUTHEAST, GridBagConstraints.SOUTH, GridBagConstraints.SOUTH-
434 Глава 12. Диспетчеры компоновки

WEST, GridBagConstraints.WEST и GridBagConstraints.NORTHWEST позво­


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

public int fill


Размеры строк и столбцов определяются содержащимися в них самым широким и
самым высоким элементами. Поле f i l l указывает, что необходимо сделать с эле­
ментом, размеры которого меньше размеров ячейки. Значение GridBagConst­
r a i n t s .NONE (по умолчанию) указывает на то, что размеры элемента должны ос­
таваться без изменений. G r i d B a g C o n s t r a i n t s . HORIZONTAL указывает, что эле­
мент должен быть расширен по горизонтали, но не по вертикали. G r i d ­
B a g C o n s t r a i n t s .VERTICAL означает, что элемент надо растянуть по вертикали,
а не по горизонтали. G r i d B a g C o n s t r a i n t s . BOTH указывает на то, что элемент
должен быть расширен как по горизонтали, так и по вертикали.

public Insets insets


Данное поле содержит объект I n s e t s , который определяет границы компонента.
Класс I n s e t s содержит единственный конструктор, которому при вызове пере­
даются параметры, определяюш[ие пространство вокруг компонента.
p u b l i c I n s e t s ( i n t t o p , i n t l e f t , i n t bottom, i n t r i g h t )

public int ipadx


public int ipady
Значения этих полей задают заполнение, добавляемое к минимальному размеру ком­
понента. Новый размер равен старому размеру плюс удвоенный размер заполнения.

public double weightx


public double weighty
Значения данных полей указывают, на какую величину компоненты должны растя­
гиваться по осям X и у, если после выбора размеров строк и столбцов остается
лишнее пространство. Значение 0.0 указывает на то, что размеры компонента не
должны изменяться. Величина 100.0 означает, что компонент должен использо­
вать дополнительное пространство совместно с другими элементами, для которых
также указан вес 100.0.

Пример использования GridBagLayout


в качестве примера рассмотрим приложение, окно которого показано на рис. 12.6.
Интерфейс приложения содержит два текстовых элемента (JTextArea и J t e x t -
f i e l d ) и три кнопки. На рисунке расположение каждого компонента обозначено за­
тененным овалом, а ячейки, в пределах которых компонент может растягиваться, —
штриховым контуром. При увеличении размеров окна текстовая область должна уве­
личиваться так, чтобы заполнять все доступное пространство. Аналогично, с увели­
чением размеров окна должно увеличиваться поле редактирования. Размеры кнопок
всегда остаются неизменными. Поскольку интерфейс достаточно сложен, для его
реализации целесообразно выбрать диспетчер компоновки GridBagLayout.
12.5. Диспетчер компоновки GridBagLayout 435

0 1 2 3

0 Текстовая
область

V J
1 [ Кнопка ^ (Г1ол€ редактироЕ ания
Z)\ Рис. 12.6. Расположение компонентов и нап­
2 (^Заполнитель J (^ Кнопка ) (^ Кнопка ) |
равление, в котором они должны расширяться

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


и ограничения для диспетчера компоновки. Для каждого из компонентов заполняют­
ся поля объекта G r i d B a g C o n t r a i n t s , а затем этот компонент включается в панель
G r i d B a g L a y o u t . В некоторых случаях значения переменных G r i d B a g C o n t r a i n t s
вычисляются относительно значений, использовавшихся для предыдущего компо­
нента. Иногда некоторые значения не переустанавливаются. П р и включении компо­
нента диспетчер G r i d B a g L a y o u t создает копию объекта G r i d B a g C o n s t r a i n t s
(клонирует объект) и сохраняет копию в переменной H a s h t a b l e , объявленной как
p r i v a t e . В качестве ключевого значения используется объект Component. Таким об­
разом, каждому компоненту ставится в соответствие свой экземпляр объекта
GridBagContraints.
Описанные возможности не реализуются ни одним из стандартных диспетчеров
компоновки за исключением G r i d B a g L a y o u t . Однако, перед тем, как сделать вывод,
что G r i d B a g L a y o u t — единственный подходящий диспетчер для решения данной за­
дачи, подумайте о том, что аналогичного эффекта можно добиться, применяя не­
сколько вложенных диспетчеров компоновки.
В данном примере для обеспечения требуемого расположения компонентов необ­
ходимо использовать заполнитель (невидимый "легковесный" компонент). Рассмот­
рим элемент Box ( х = 1 , у=2). Этот элемент лишь занимает место, гарантируя тем са­
мым, что в столбце 1 содержится хотя бы один компонент фиксированной ширины.
Без компонента Box ширина столбца 1 была бы принята равной О, поскольку поле ре­
дактирования — расширяемый элемент и его размеры по горизонтали не фиксирова­
ны. Оставшееся пространство было бы равномерно распределено между столбцами 2
и 3, а кнопки Ок и Exit были бы расположены так, как показано на рис. 12.8. Как вы
увидите далее в этой главе, при использовании вложенных контейнеров с разными
диспетчерами компоновки необходимость в заполнителях отпадает.

Листинг 1 2 . 7 . G r i d B a g T e s t . J a v a

import java.awt.*;
import Java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;

/' Пример, демонстрирующий использование GridBagLayout


436 Глава 12. Диспетчеры компоновки

* для размещения элементов ввода текста и нескольких


* кнопок.
^/
public class GridBagTest extends JPanel {
private JTextArea textArea;
private JButton bSaveAs, bOk^ bExit;
private JTextField fileField;
private GridBagConstraints c;

public GridBagTest0 {
setLayout(new GridBagLayout());
setBorder(BorderFactory.createEtchedBorder());

textArea = new JTextArea(12,40); // 12 rows, 40 cols


bSaveAs = new JButton("Save A s " ) ;
fileField = new JTextField("C:WDocument.txt");
bOk = new JButton("OK");
bExit = new JButton("Exit");

с = new GridBagConstraints0;

// Текстовая область.
с.gridx = О;
c.gridy = 0;
с.gridwidth = GridBagConstraints.REMAINDER;
с.gridheight = 1;
с.weightx = 1.0;
с.weighty = 1.0;
с.fill = GridBagConstraints.BOTH;
c.insets = new Insets(2,2,2,2); //t,l,b,r
add(textArea,c);

// кнопка Save A s .
с .gridx = 0;
с .gridy = 1;
с .gridwidth = 1;
с .gridheight = 1;
с .weightx = 0. 0
с .weighty = 0. 0
с .fill =
= Giri<
GridBagConstraints.VERTICAL;
асid(bSaveAs, c) .

// Поле редактирования.
с.gridx = 1;
с.gridwidth = GridBagConstraints.REMAINDER;
с.gridheight = 1;
с.weightx = 1.0;
с.weighty = 0.0;
c.fill = GridBagConstraints.BOTH;
add(fileField, c) ;

// Кнопка OK.
с.gridx = 2;
c.gridy++;
12.5. Диспетчер компоновки GridBagLayout 437

с.gridwidth = 1;
c.gridheight = 1;
с.weightx = 0.0;
с.weighty = 0.0;
с.fill = GridBagConstraints.NONE;
add(bOk,c) ;

// Кнопка Exit.
c.gridx = 3;
c.gridwidth = 1;
c.gridheight = 1;
с.weightx = 0.0;
с.weighty = 0.0;
c.fill = GridBagConstraints.NONE;
add(bExit,c);
// Заполнитель, используемый для того, чтобы
// столбец 1 имел ненулевую ширину.
Component filler = Box.createRigidArea(new Dimension(1,1))
c.gridx =1;
с.weightx = 1.0;
add(filler,c);

public static void main(String[] args) {


WindowUtilities.setNativeLookAndFeel();
JFrame frame = new JFrame("GrigBagLayout Test");
frame.setContentPane(new GridBagTest());
frame.addWindowListener(new ExitListener());
frame.pack();
frame.setVisible(true);
}

I^GiigBagLayoul Test . :;: ;:«Ш;Ш|1жЖ2| Himfflfiffllff!


1

Save As jCADocunfior.tM Save As I CADocumentt4l

OK 1 P ^ 11 OK I

Рис. 12.7. Используя объект GridBag­ Рис. 12.8. Так выглядит контейнер с
Cons t r a i n t s , можно осуществлять диспетчером компоновки GridBag­
контроль за расположением элементов, Layout, если в столбце 1 отсутствует
однако код программы становится элемент Box
достаточно сложным
438 Глава 12. Диспетчеры компоновки

Конструктор GridBagLayout
p u b l i c GridBagLayoutO
Диспетчер компоновки G r i d B a g L a y o u t содержит только один конструктор. Осо­
бенности размещения компонентов задаются с помощью объекта G r i d B a g ­
Constraints.

Методы GridBagLayout
Диспетчер G r i d B a g L a y o u t содержит следующие методы.

p u b l i c GridBagConstraints g e t C o n s t r a i n t s ( C o m p o n e n t с)
Данный метод возвращает копию объекта G r i d B a g C o n s t r a i n t s , который был
использован при включении указанного компонента в состав контейнера.

public float getLayoutAlignmentX(Container с)


public float getLayoutAlignmentY(Container с)
Данные методы позволяют определить тип выравнивания для контейнера. Под­
робно этот вопрос будет рассмотрен в разделе 13.2.

public int[ ][ ] getLayoutDimensionsO


Метод g e t L a y o u t D i m e n s i o n s возвращает массив, содержащий размер каждой
строки и столбца в окне.

p u b l i c P o i n t getLayoutOrigin()
Данный метод возвращает расположение левого верхнего угла G r i d B a g L a y o u t
относительно окна.

public d o u b l e [ ][ ] getLayoutWeights()
Метод g e t L a y o u t W e i g h t s возвращает значения w e i g h t x и w e i g h t y .

public v o i d s e t C o n s t r a i n t s ( C o m p o n e n t c o m p o n e n t , GridBagConstraints
constraints)
Данный метод регистрирует ограничения для указанного компонента. Этот метод
применяется редко, поскольку гораздо удобнее задать ограничения при включе­
нии компонента в контейнер. Так, например, вместо
container.add(component);
layout.setConstraints(constraints);
удобнее использовать выражение
container.add(component, constraints);
12.6. Диспетчер ком поновки BoxLayout 439

12.6. Диспетчер компоновки BoxLayout


B o x L a y o u t — новый диспетчер компоновки, содержащийся в пакете Swing. Он по­
зволяет размещать компоненты по строкам ( B o x L a y o u t . X A X I S ) либо по столбцам
( B o x L a y o u t . Y A X I S ) . Диспетчер B o x L a y o u t размещает компоненты в том порядке,
в котором они включаются в контейнер: слева направо — при горизонтальном распо­
ложении и сверху вниз — при вертикальном расположении. Изменение размеров кон­
тейнера не приводит к изменению расположения компонентов.
Диспетчер B o x L a y o u t пытается сохранить первоначальную ширину компонентов
(при вертикальном расположении) либо их первоначальную высоту (при горизон­
тальном расположении). Для вертикального расположения, если компоненты имеют
различную ширину, B o x L a y o u t растягивает все компоненты по ширине до размера
самого широкого из них. Если сделать это не удается (максимальный размер компо­
нента ограничен), B o x L a y o u t выравнивает компоненты по горизонтали в соответст­
вии с типом выравнивания по оси х, заданным для контейнера.
Аналогично, при горизонтальном расположении B o x L a y o u t старается сохранить
первоначальную высоту компонента. Если компоненты имеют различную высоту,
BoxLayout растягивает все компоненты по высоте до размера самого высокого из них.
Если же сделать это не удается, диспетчер компоновки выравнивает компоненты по
вертикали в соответствии с типом выравнивания по оси у, заданным для контейнера.
Каждый из "легковесных" Swing-компонентов, являющийся дочерним по отноше­
нию к J C o m p o n e n t , позволяет установить значение выравнивания в интервале от O.Of
до l.Of, где 0.0— расположение компонента, наиболее близкое к началу координат, а
1.0 — самая удаленная от начала координат позиция в контейнере. Кроме того, в клас­
се Component определено пять констант, с помощью которых может задаваться тип
выравнивания: C o m p o n e n t . LEFT_ALIGNMENT (0.0), Component .CENTER_ALIGNMENT
(0.5), C o m p o n e n t . RIGHT_ALIGNMENT (1.0), Component .TOP_ALIGNMENT (0.0) и
C o m p o n e n t . BOTTOM_ALIGNMENT (1.0). В зависимости от ориентации B o x L a y o u t , тип
выравнивания каждого из компонентов J C o m p o n e n t может быть задан с помощью
указанных ниже методов, где Ххл: представляет относительную позицию.

JComponent.setAlignmentX(Component.Xxx_ALIGNMENT);
JComponent.setAlignmentY(Component.Xxx_ALIGNMENT);
Для большинства Swing-компонентов по умолчанию задано горизонтальное вы­
равнивание по центру (Component .CENTER_ALIGNMENT). Компоненты J B u t t o n ,
JComboBox, J L a b e l и JMenu являются исключением из общего правила и выравни­
ваются по левому краю ( C o m p o n e n t . LEFT_ALIGNMENT).
Простой пример использования диспетчера компоновки B o x L a y o u t с вертикаль­
ным расположением ( B o x L a y o u t . Y_AXIS) представлен в листинге 12.8. В данном
примере фрагмент статического текста расположен в верхней части окна и по умол­
чанию выравнивается по левому краю. Н и ж е текста находятся три кнопки. П р и акти­
визации одной из них вызывается метод, который изменяет тип выравнивания для
всех трех кнопок и перестраивает содержимое контейнера (вызывает метод
r e v a l i d a t e ) . Для статического текста тип выравнивания не изменяется.
440 Глава 12. Диспетчеры компоновки

Листинг 12.8. BoxLayoutTest. Java

import java.awt.*;
import Java.awt.event.*;
import javax.swing.*;
/** Пример использования BoxLayout. */

public class BoxLayoutTest extends Jpanel


implements ActionListener{
BoxLayout layout;
JButton topButton, middleButton, bottomButton;

public BoxLayoutTest() {
layout = new BoxLayout (this, BoxLayout .Y__AXIS) ;
s e tLayou t (1 ayou t) ;

JLabel label = new JLabel("BoxLayout Demo");


topButton = new JButton("Left A l i g n m e n t " ) ;
middleButton = new JButton("Center A l i g n m e n t " ) ;
bottomButton = new JButton("Right A l i g n m e n t " ) ;
topButton.addActionListener(this);
middleButton.addActionListener(this) ;
bottomButton.addActionListener(this);

add(label);
add(topButton);
add(middleButton);
add(bottomButton);
setBackground(Color.white);
}
public void actionPerformed(ActionEvent event) {
if (event.getSource() == topButton) {
refresh (Component. LEFT__ALIGNMENT) ;
} else if (event.getSource() == middleButton) {
refresh (Component. CENTER__ALIGNMENT) ;
} else if (event.getSource() == bottomButton) {
refresh(Component.RIGHT_ALIGNMENT);
}
}
private void refresh(float a l i g n m e n t ) !
topButton.setAlignmentX(alignment);
middleButton.setAlignmentX(alignment);
bottomButton. setAlignmentX (alignment) ;
revalidate();
System.out.println("x: "+layout.getLayoutAlignmentX(this));
}
public static void main(String[] args) {
WindowUtilities.setNativeLookAndFeel();
WindowUtilities.OpenlnJFrame(new BoxLayoutTest(), 300, 135,
"BoxLayoutTest");
12.6. Диспетчер компоновки BoxLayout 441

На рис. 12.9 показан внешний вид контейнера, использующего B o x L a y o u t после


активизации кнопок Left Alignment и Right Alignment. Данный пример иллюстрирует
проблему, которая возникает в том случае, если компоненты имеют различный тип
выравнивания.
На рис. 12.9,а все компоненты выравниваются по левому краю и отображаются
корректно. Однако на рис. 12.9,6 три кнопки выравниваются по правому краю, в то
время как для статического текста по-прежнему используется левое выравнивание.
Как видно на рисунке, в этом случае компонент больше не выравнивается относи­
тельно контейнера; теперь выравнивание осуществляется относительно других ком­
понентов в контейнере. В такой ситуации значение выравнивания (0.0— 1.0) пред­
ставляет часть компонента, расположенную слева от общей "компонентной" оси.
В данном примере слева от оси находится 0% статического текста (значение 0.0) и
100% каждой кнопки (значение 1.0). Расположение "компонентной" оси относитель­
но контейнера вычисляется на базе ширины компонентов и типов выравнивания, ус­
тановленных для них. Ч т о б ы не получить результаты, подобные показанным на
рис. 12.9, мы рекомендуем всегда задавать выравнивание для каждого компонента и
указывать тот же тип выравнивания при создании B o x L a y o u t .

^::.:].|ми|р>ишм llllllll^ i
BoxLayout Demo BoxLayout Demo

|Lj-E!:^^?P'!*^„j| 1 LeftAiignment

Center Mgnment j | Center Alignment

Right ^«ignment f Rigiit Alignment '

(а) (6)
Рис. 12.9. (a) Расположение компонентов с помощью BoxLayout, если
для всех компонентов установлено значение выравнивания 0.0 (вырав­
нивание по левому краю); (б) Расположение компонентов с помощью
BoxLayout, если выравнивание для статического текста равно 0.0, а для
кнопок— 1.0

П р и вертикальном расположении компонентов B o x L a y o u t изменяет ширину всех


элементов в соответствии с размерами самого широкого из них. Однако, если макси­
мальные размеры компонентов не ограничены (например, для них задано значение
I n t e g e r . MAX_VALUE), ширина каждого компонента будет равна ширине контейнера.
Если для всех компонентов задана неограниченная ширина, а для одного из них ука­
зано строгое выравнивание по левому краю (значение 0.0) или по правому краю
(значение 1.0), контейнер поведет себя не совсем привычно для разработчика. В этом
случае ширина компонента со строгим выравниванием будет меньше ширины кон­
тейнера, а ширина остальных компонентов будет равна ширине контейнера. На
практике размеры почти всех компонентов ограничены, поэтому описанные особен­
ности поведения B o x L a y o u t остаются незамеченными. Изменить максимальные раз­
меры компонента можно с помощью следующего выражения:
component.setMaximumSize(new Dimension(width, height));
Bee вышесказанное справедливо и для высоты элементов при их горизонтальном
расположении.
442 Глава 1 2 . Д и с п е т ч е р ы компоновки

Конструктор BoxLayout
Для диспетчера компоновки BoxLayout определен только один конструктор.

public BoxLayout(Container container, int axis)


Первый параметр, передаваемый конструктору, представляет собой ссылку на
объект C o n t a i n e r , которым должен управлять BoxLayout. Ссылка на C o n t a i n e r
хранится в переменной, объявленной как p r i v a t e , и проверяется при вызове
других методов BoxLayout. Таким образом, каждый диспетчер BoxLayout может
быть связан только с одним контейнером. Параметр a x i s задает размещение ком­
понентов: BoxLayout .XAXIS соответствует горизонтальному расположению
(слева направо), а BoxLayout. Y_AXIS — вертикальному (сверху вниз).

Внимание!

В отличие от других стандартных диспетчеров компоновки, BoxLayout


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

Методы BoxLayout
Ниже описаны наиболее часто используемые методы BoxLayout.

public Dimension preferredLayoutSize(Container container)


public Dimension minimumLayoutSize(Container container)
public Dimension maximumLayoutSize(Container container)
Данные методы возвращают соответственно нормальный, минимальный и макси­
мальный размеры, необходимые для того, чтобы разместить в контейнере требуе­
мые компоненты.

public float getLayoutAlignmentX(Container container)


public float getLayoutAlignmentY(Container container)
Данные методы возвращают общие типы выравнивания по оси х и у для контейне­
ра (который также является компонентом) относительно других компонентов в
окне. Значение выравнивания лежит в пределах от O.Of до l.Of. Подробно вопросы
выравнивания будут рассмотрены в разделе 13.2.

public void layoutContainer(Container container)


Данный метод управляет размерами и размещением всех компонентов в контей­
нере, вызывая для каждого компонента метод s e t Bounds.

public void invalidateLayout(container container)


Данный метод отменяет все сведения о размещении компонентов, содержащиеся
в кэше.
12.7. Размещение компонентов вручную 443

Заметьте, что Swing также предоставляет контейнер Box, управляемый диспетче­


ром компоновки BoxLayout. Конструктор Box позволяет задавать ориентацию осей с
помощью констант BoxLayout .XAXIS для горизонтального расположения элемен­
тов и BoxLayout .YAXIS — для вертикального расположения. Класс Box также со­
держит многочисленные статические методы для создания невидимых компонентов-
заполнителей.

12.7. Размещение компонентов вручную


Диспетчеры компоновки призваны облегчить выполнение разработчиком постав­
ленной перед ним задачи. Однако, если вы обнаружите, что вместо помощи диспет­
чер мешает работе, можете отключить его. Это вполне допустимо; со временем вы
заметите, что такой подход используется гораздо чаще, чем вы могли предположить
вначале. Если вместо диспетчера компоновки указано значение n u l l , то размеры и
расположение компонентов могут и даже должны быть заданы вручную. Для установ­
ки размеров и позиции используются методы s e t S i z e (width, height),
s e t L o c a t i o n ( l e f t , top) и s e t B o u n d s ( l e f t , t o p , w i d t h , h e i g h t ) . Как вы
знаете, размеры аплетов не изменяются, но для Frame и JFrame вы можете переоп­
ределить методы s e t S i z e и s e t Bounds. В теле переопределенных методов обычно
вызываются соответственно s u p e r . s e t S i z e и s u p e r . setBounds, а также коррек­
тируются позиции компонентов.
Вычисление вручную координат при отключенном диспетчере компоновки — ру­
тинная работа, однако если в контейнере содержится небольшое число компонентов
(возможно, этот контейнер включен как компонент в другое окно), то данная задача
вполне разрешима. Пример работы при отключенном диспетчере компоновки при­
веден в листинге 12.9, а результаты выполнения кода показаны на рис. 12.10. Как бу­
дет сказано далее в этой главе, при отключенном диспетчере компоновки вы можете
вычислять относительные позиции компонентов. Данный подход обеспечивает
большую гибкость при относительно малом объеме дополнительной работы.

Листинг 12.9. N u l l T e s t . J a v a

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
/** Диспетчеры компоновки удобны в использовании, однако
* разработчик <В>не обязан</В> применять их.
* Чтобы отключить диспетчер компоновки, надо в качестве
* параметра метода setLayout задать значение n u l l .
Ч
public c l a s s NullTest extends Applet {
p u b l i c void i n i t O {
setLayout(null);
Button bl = new Button("Button 1")
Button b2 = new Button("Button 2")
Button b3 = new Button("Button 3")
Button b4 = new Button("Button 4")
Button b5 = new Button("Button 5")
444 Глава 12. Диспетчеры компоновки

bl.setBounds(О, О, 150, 50);


b2.setBounds(150, О, 75, 50);
b3.setBounds(225, О, 75, 50);
b4.setBounds(25, 60, 100, 40);
b5.setBounds(175, 60, 100, 40) ;
add(bl)
add(b2)
add(b3)
add(b4)
add(b5)

Ш Applet Viewer: NuliTest.class РЦшЕЗ


Apple»

Sutton 2 Button 3

Рис. 12.10. Заменив ссылку на диспетчер компоновки


значением n u l l , вы можете задавать расположение
Applet started.
элементов вручную

12.8. Эффективное использование


диспетчеров компоновки
Использование диспетчера компоновки освобождает разработчика от выполнения
таких рутинных задач, как вычисление размеров и координат элементов, и упрощает
процесс создания интерфейсов. Однако на практике при создании Java-программ неко­
торые разработчики обнаруживают, что диспетчер компоновки скорее мешает, чем по­
могает в работе. Так, например, для реализации диспетчера G r i d B a g L a y o u t и разме­
щения компонентов необходимо затратить слишком большие усилия. Существует не­
сколько основных стратегий, позволяющих наиболее эффективно применять дис­
петчеры компоновки при создании пользовательских интерфейсов.

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

Отключение диспетчеров компоновки для некоторых контейнеров


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

Создание с о б с т в е н н ы х д и с п е т ч е р о в к о м п о н о в к и
Если вам не подходит ни один из стандартных диспетчеров компоновки, создайте
собственный.
12.8. Эффективное использование диспетчеров компоновки 445

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

Использование вложенных контейнеров


Вместо того чтобы пытаться разместить компоненты, пользуясь одним диспетче­
ром компоновки, постарайтесь разбить окно на несколько прямоугольных областей.
В каждую из областей включите отдельную панель со своим диспетчером компоновки
и разместите в них требуемые элементы. Примеры расположения компонентов с по­
мощью различных диспетчеров компоновки были приведены в предыдущих разделах
этой главы. Несмотря на то что диспетчер G r i d B a g L a y o u t позволяет реализовать
практически любое размещение компонентов в окне, из листинга 12.10 видно, что эту
же задачу можно решить гораздо проще, разместив в различных областях окна от­
дельные контейнеры, с которыми связаны различные диспетчеры компоновки.
На рис. 12.11 демонстрируется принцип использования вложенных контейнеров с
различными диспетчерами компоновки. Следуя такому подходу, необходимо пом­
нить, как стандартные диспетчеры компоновки изменяют размеры компонентов.

• F l o w L a y o u t — сохраняет исходные размеры компонентов.


• G r i d L a y o u t — изменяет размеры компонентов в соответствии с размерами
ячеек.
• B o r d e r L a y o u t — в областях NORTH и SOUTH сохраняется исходная высота эле­
ментов, а в областях EAST и WEST — их исходная ширина. В области CENTER из­
меняется как высота, так и ширина компонента.
Исходя из описанных особенностей выберем диспетчеры компоновки для компо­
нентов, которые размещены в окне, показанном на рис. 12.11. Окно редактирования
текста может расширяться по горизонтали и вертикали, заполняя все дост)'пное про­
странство; в этом случае исходные размеры не должны сохраняться. В нижней части
окна одна из кнопок связана с полем редактирования. Ш и р и н а и высота кнопки
должны оставаться неизменными, а поле редактирования может расширяться по го­
ризонтали. Таким образом, с первым из контейнеров должен быть связан диспетчер
B o r d e r L a y o u t , для которого области EAST, NORTH и WEST остаются пустыми.
Нижняя часть окна разделена на две строки, следовательно, для нее подходлт кон­
тейнер J P a n e l с диспетчером компоновки G r i d L a y o u t , при создании которого за­
даны две строки и один столбец. В каждую из строк включается конте11нер JPan€3l,
содержащий поле редактирования и кнопки. Чтобы выбрать диспетчеры компоаовки
для каждого из J P a n e l , рассмотрим требование к компонентам. Кнопка Save As
(компонент J B u t t o n ) должна сохранить свои исходные размеры и всегда находиться
в левой части J P a n e l . Для этого контейнера, как было сказано выше, можно исполь­
зовать диспетчер B o r d e r L a y o u t . Диспетчер F l o w L a y o u t , связанный с J P a n e l по
умолчанию, сохраняет исходные размеры кнопок, но располагает их по центру кон­
тейнера. Чтобы кнопки располагались в правой части панели, достаточно поставить в
соответствие J P a n e l диспетчер F l o w L a y o u t с типом выравнивания F l o w L a y o u t .
RIGHT. Указанное сочетание диспетчеров компоновки— лишь одно из возможных
446 Глава 12. Диспетчеры компоновки

решений данной задачи. На этом примере хорошо видно, что код, реализующий вло­
женные контейнеры, гораздо проще кода, приведенного в листинге 12.7, где для раз­
мещения элементов используется один диспетчер компоновки GridBagLayout. Ре­
зультат выполнения программы показан на рис. 12.12.

Текстовая область

Рис. 12.11. Для размещения компонентов


IBorderLayout
FlowLayout используются вложенные контейнеры с
GridLayout различными диспетчерами компоновки

Листинг 12.10.NestedLayout.Java

import java.awt.*;
import Java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;

/** Пример, демонстрирующий использование вложенных


* контейнеров для размещения ко1^понентов.

public class NestedLayout extends JPanel {

private JTextArea textArea;


private JButton bSaveAs, bOk, bExit;
private JTextField fileField;

public NestedLayout() {

setLayout(new BorderLayout(2,2));
setBorder(BorderFactory.createEtchedBorder0);

textArea = new JTextArea(12,40); // 12 строк, 40 столбцов


bSaveAs = new JButton("Save A s " ) ;
fileField = new JTextField("C:WDocument.txt");
bOk = new JButton("OK");
bExit = new JButton("Exit") ;
12.8. Эффективное использование диспетчеров компоновки 447

add(textArea,BorderLayout.CENTER);

// Размещение кнопок и поля редактирования


/ / в нижней панели.
JPanel bottomPanel = new JPanelO;
bottomPanel.setLayout(new GridLayout(2,1));

JPanel subPanell = new JPanelO;


JPanel subPanel2 = new JPanelO;
siibPanell. setLayout (new BorderLayout ()) ;
subPanel2.setLayout(new FlowLayout(FlowLayout.RIGHT,2,2))

subPanell.add(bSaveAs,BorderLayout.WEST) ;
subPanell.add(fileField,BorderLayout.CENTER);
subPanel2.add(bOk) ;
subPanel2.add(bExit);

bottomPanel.add(subPanell);
bottomPanel.add(subPanel2);

add(bottomPanel,BorderLayout.SOUTH);

public static void main(String[] args) {


WindowUtilities.setNativeLookAndFeel();
JFrame frame = new JFrame("Nested Containers")
frame.setContentPane(new NestedLayout());
frame.addWindowListener(new ExitListener());
frame.pack();
frame.setvisible(true) ;
}

ШШШШШШШЩ
1

|11|||:|Ш|||с:Юоситеп1.1|(1 ;| Рис. 12.12. Вложенные

^^^шjlljlll IMili ||И1| контейнеры упрощают процесс


создания сложных интерфейсов
448 Глава 12. Диспетчеры компоновки

Отключение диспетчеров компоновки


для некоторых контейнеров
Размещение компонентов вручную не всегда является сложной задачей, особенно
если данный подход сочетается с использованием вложенных контейнеров. Предпо­
ложим, например, что вы хотите создать окно аплета, в левой части которого распо­
лагался бы вертикальный ряд кнопок, а остальная часть окна использовалась бы для
рисования (в качестве заголовка применяется компонент Label). Предположим так­
же, что кнопки должны занимать ровно 4 0 % ширины области аплета (это достаточно
для размещения самых широких кнопок). Решить такую задачу можно, создав два объ­
екта Panel и расположив их в области аплета с помощью метода setBounds; при
этом диспетчер компоновки для аплета должен быть отключен (листинг 12.1). По­
скольку с каждым из контейнеров Panel связан диспетчер компоновки, элементы в
них размещаются автоматически (рис. 12.13).

Листинг 12.11.ButtonCol.Java

import Java.applet.Applet;
import java.awt.*;

/*^ Пример размещения компонентов без использования


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

public class ButtonCol extends Applet {


public void initO {
setLayout(null);
int widthl = getSize0 .width*4/10,
width2 = getSize0 .width - widthl,
height = getSize().height;
Panel buttonPanel = new Panel();
buttonPanel.setBounds(0, 0, widthl, height);
buttonPanel.setLayout(new GridLayout(6, 1));
buttonPanel.add(new Label("Buttons", Label.CENTER));
buttonPanel.add(new Button("Button One"));
buttonPanel.add(new Button("Button Two"));
buttonPanel.add(new Button("Button Three"));
buttonPanel.add(new Button("Button Four"));
buttonPanel.add(new Button("Button Five"));
add(buttonPanel);
Panel everythingElse = new Panel();
everythingElse.setBounds(widthl+1, 0, width2, height);
everythingElse.add(new Label("Everything Else"));
add(everythingElse);
12.8. Эффективное использование диспетчеров компоновки 449

^.||.1.!Ш||^!1!|1!'Л|!Д'1?Ц||1.^.|'ИШ

Buttons Everything Else

Button One

BirttonTwo

Button four
Рис. 12.13. Контейнеры в окне верхнего
ByttonFw
уровня размещаются вручную, а элементы
внутри контейнеров — автоматически

Резервирование пустого пространства вокруг


компонентов
И з м е н е н и е установок п о умолчанию д л я д и с п е т ч е р о в к о м п о н о в к и
Все стандартные диспетчеры компоновки AWT за исключением G r i d B a g L a y o u t ,
содержат вариант конструктора, позволяющий задать интервал между компонента­
ми. П р и работе с G r i d B a g L a y o u t размеры пустого пространства определяются по­
средством поля i n s e t s либо полей i p a d x и i p a d y объекта G r i d B a g C o n s t r a i n t s .

Определение нового значения i n s e t s для контейнера


Объект I n s e t s задает размеры пустого пространства рядом с границами контей­
нера. Этот объект обрабатывают встроенные диспетчеры компоновки; то же
должны делать и диспетчеры, создаваемые разработчиками. Объект I n s e t s также
может использоваться для создания рамок вокруг компонентов. К сожалению,
объект C o n t a i n e r не позволяет изменять значение соответствующего поля, по­
этому, для того чтобы воспользоваться возможностями, предоставляемыми объек­
том I n s e t s , следует создать подкласс одного из контейнеров (например, класса
P a n e l ) и переопределить метод g e t l n s e t s .

И с п о л ь з о в а н и е о б ъ е к т о в C a n v a s и B o x в качестве н е в и д и м ы х заполнителей
В AWT объект C a n v a s , который не обрабатывает события, связанные с мышью и
на котором не выполняется рисование, может рассматриваться как "пустой" эле­
мент, занимающий определенную часть окна. C a n v a s часто используется для ре­
зервирования пространства при работе с диспетчерами компоновки F l o w L a y o u t ,
G r i d L a y o u t , G r i d B a g L a y o u t , B o x L a y o u t либо в областях NORTH,SOUTH, EAST и
WEST диспетчера B o r d e r L a y o u t .
П р и работе со Swing-компонентами в качестве невидимого заполнителя может
применяться объект Box. Класс Box выполняет следующие функции.

• Фиксированная область — двухмерный невидимый компонент ( B o x . c r e a t e -


R i g i d A r e a ) с заданными шириной и высотой.
• "Подпорка"— одномерный невидимый компонент, который может быть
либо горизонтальным, фиксированной ш и р и н ы и нулевой высоты ( В о х . -
C r e a t e H o r i z o n t a l S t r u t ) , либо вертикальным, нулевой ширины и фикси­
рованной высоты ( B o x . C r e a t e H o r i z o n t a l S t r u t ) .
450 Глава 1 2 . Д и с п е т ч е р ы компоновки

• Связывающий элемент— одномерный (Box. c r e a t e H o r i z o n t a l G l u e , B o x . -


c r e a t e V e r t i c a l G l u e ) или двухмерный ( B o x . c r e a t e G l u e ) невидимый ком­
понент, который расширяется и заполняет все оставшееся пространство.

Листинг 12.12 иллюстрирует использование фиксированной области, горизон­


тальной "подпорки" и горизонтального связывающего элемента. В данном примере
создается четыре панели J P a n e l s (по умолчанию с ними связывается диспетчер ком­
поновки F l o w L a y o u t ) . В каждой из панелей содержатся две кнопки. Как показано на
рис. 12.14, в первую панель между кнопками включается фиксированная область раз­
мерами 20x75 пикселей. Во вторую панель для разделения кнопок помещается
"подпорка" нулевой высоты и ш и р и н о й 60 пикселей. В третьей панели предпринима­
ется попытка использовать связывающий элемент для заполнения пространства меж­
ду кнопками, однако эта попытка оканчивается неудачей, поскольку диспетчер
F l o w L a y o u t игнорирует максимальный размер элемента Box. Единственным диспет­
чером, который обрабатывает максимальный размер данного элемента, является
B o x L a y o u t . Четвертая панель, с которой связан B o x L a y o u t , отображает ожидаемый
результат использования горизонтального связывающего элемента.
Внимание!

Связывающий элемент может применяться при работе с диспетче­


ром компоновки, обрабатывающим максимальный размер компо­
нента. В частности, данный элемент можно использовать совместно
с BoxLayout.

Листинг 12.12.InvisibleComponentTest.Java

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

public class InvisibleComponentTest extends JPanel {


Component spacer;

public InvisibleComponentTest() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

// Между двумя кнопками помещается фиксированная


// область -невидимый компонент шириной 25 пикселей
/ / и высотой 75 пикселей.
JPanel р1= new JPanel();
spacer = Box.createRigidArea(new Dimension(20,75));
setUpPanel(pi, "Rigid Area - 20x75 pixels", spacer);

// Разделение кнопок с помощью горизонтальной "подпорки"


// размером 60 пикселей.
JPanel р2= new JPanel();
spacer = Box.createHorizontalStrut(60);
setUpPanel(p2, "Horizontal Strut - 60 pixels", spacer);
12.8. Эффективное использование диспетчеров компоновки 451

// Горизонтальный связывающий элемент: при работе


// с FlowLayout не производит должного эффекта.
JPanel рЗ= new JPanelO;
spacer = Box.createHorizontalGlueО;
setUpPanel(рЗ, "Horizontal Glue - FlowLayout", spacer);
// Использование связывающего элемента для заполнения
// пространства между двумя кнопками. Поскольку связывающий
// элемент не поддерживается FlowLayout, диспетчер компоновки
// для JPanel изменяется на BoxLayout.
JPanel р4= new JPanelO;
р4.setLayout(new BoxLayout(p4,BoxLayout.X-AXIS));
spacer = Box.createHorizontalGlue0;
setUpPanel(p4, "Horizontal Glue - BoxLayout", spacer);
add(pi);
add(p2);
add(p3);
add{p4);
}
// Вспомогательный класс для установки обрамления
/ / и добавления компонентов.
private void setUpPanel(JPanel p, String title,
Component spacer) {
p.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(),title,
TitledBorder.TOP,TitledBorder.CENTER));
.setBackground(Color.white);
.add(new JButton("Left"));
.add(spacer);
.add(new JButton("Right"));
}
public static void main(String[] args) {
String title = "Using Invisible Components";
WindowUtilities.setNativeLookAndFeel();
WindowUtilities.openlnJFrame(new InvisibleComponentTest(),
350, 325, title);
}

||||"^|ri|x|
Rigid Area - 20x75 pixels

ILSLJI ^'sht 1

Horizontal Strut- 60 pixels

iM 1 . R^ j
Horizontal Glue - FlowLayout

Left 1 mm 1
Horizontal Glue - BoxLayout
Рис. 12.14. Невидимый элемент Box
позволяет управлять расположением
Left 1 Right 1
компонентов
452 Глава 12. Диспетчеры компоновки

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


Класс Box содержит следующие статические методы, позволяющие создавать не­
видимые компоненты.

public static Component createRigidArea(Dimension size)


Данный метод создает невидимый компонент фиксированных размеров.

public static Component createHorizontalStrut(int width)


public static Component createVerticalStrut(int height)
Первый из указанных методов создает невидимый компонент фиксированной ши­
рины и нулевой высоты. Второй метод создает невидимый компонент фиксиро­
ванной высоты и нулевой ширины.

public static Component createHorizontalGlue()


public static Component createVerticalGlue()
public static Component createGlue()
Первых два метода создают невидимый связывающий элемент, который может
расширяться по горизонтали или по вертикали, заполняя все доступное простран­
ство. Третий метод создает связывающий элемент, который может расширяться в
обоих направлениях. Эффект заполнения достигается за счет установки макси­
мального размера, равного S h o r t .MAX_VALUE. Если в контейнере, с которым свя­
зан диспетчер BoxLayout, находится больше одного связывающего элемента, сво­
бодное пространство делится между этими элементами поровну.

12.9. Резюме
Диспетчеры компоновки позволяют размещать элементы в окне. Использовать их
очень удобно в тех случаях, когда необходимо изменять размеры окна, добавлять
компоненты и переносить программу на другие платформы. Чаще всего используют­
ся диспетчеры компоновки FlowLayout, BorderLayout, GridLayout, CardLayout
и GridBagLayout. В Java 2 для обеспечения гибкости при разработке программ до­
бавлен дополнительный диспетчер BoxLayout. Если ни один из диспетчеров не под­
ходит вам, можете отключить их и размещать компоненты вручную, однако не забы­
вайте, что практически любую задачу создания интерфейса можно решить с помощью
вложенных контейнеров.
К настоящему времени вы уже имеете сведения об окнах и даже познакомились с
диспетчерами компоновки. Однако окна надо чем-то заполнять. В главе 13 вы узнаете
о кнопках, флажках и переключателях опций, полосах прокрутки и других компонен­
тах графического пользовательского интерфейса.
КОМПОНЕНТЫ AWT

В ЭТОЙ главе...

• Класс Component.

• Создание "легковесных" компонентов.

• Основные типы окон: P a n e l i A p p l e t , Frame и D i a l o g .

• Завершение работы с окном Frame в аплете,

• Создание меню в окне Frame.

• Создание окон с возможностью прокрутки.

• Использование сериализации для сохранения объектов на


диске и последующей загрузки.

• Обработка событий, связанных с управляющими элемен­


тами графического интерфейса.

• Основные типы интерфейсных элементов.


Jy\ZJ^ZJ

акет Abstract Window Toolkit (AWT), представленный в первой реализации

П Java, содержит основные средства для построения окон и и н т е р ф е й с н ы х эле­


ментов, необходимых для создания программ с графическим пользователь­
ским интерфейсом. С появлением платформы Java 2 на смену AWT пришли Swing-
компоненты (они будут подробно рассмотрены в главах 14, 15). К сожалению, из всех
популярных броузеров в настоящее время только Netscape 6 поддерживает Swing.
Чтобы аплеты, использующие Swing, выполнялись в более ранних версиях Netscape
либо в Internet Explorer, на компьютере необходимо инсталлировать Java Plug-In или
копировать Swing-компоненты в виде JAR-файла по сети. В данной главе мы рассмот­
рим основные типы окон и управляющих элементов, предоставляемых AWT, и обсу­
дим особенности обработки событий, связанных с компонентами пользовательского
интерфейса. Java 1.1 AWT API поддерживается Netscape 4.06 и Internet Explorer 4, a
также более поздними версиями этих броузеров.
AWT позволяет создавать восемь основных типов окон, применяемых при создании
пользовательских интерфейсов: C a n v a s , P a n e l , A p p l e t , S c r o l l P a n e , Frame, D i a l o g ,
F i l e D i a l o g и Window. В некоторых окнах (например, P a n e l и S c r o l l P a n e ) отсутству­
ет обрамление, поэтому они могут размещаться лишь внутри других окон. Такие окна,
как Frame и D i a l o g , содержат обрамление и заголовок и могут отображаться в любом
месте экрана. Основу аплета составляет окно A p p l e t ; именно благодаря аплетам ком­
поненты AWT продолжают использоваться. Базовым окном для графических приложе­
ний является Frame. В отличие от аплетов, в независимых графических приложениях в
основном применяются компоненты Swing, однако класс Frame продолжает играть
важную роль при создании Java-программ. В частности, окно Frame может быть откры­
то из аплета, что позволяет создавать многооконный интерфейс.
В последующих разделах мы опишем каждый из восьми типов окон, их назначе­
ние, стандартные диспетчеры компоновки, действия по созданию окон, а также при­
меры их использования. Кроме того, в данной главе будут рассмотрены классы
Component и C o n t a i n e r , на базе которых создаются окна.
После обсуждения основных типов окон мы рассмотрим общепринятый подход к
обработке событий, а затем перейдем к изучению основных управляющих элементов,
456 Глава 13. Компоненты AWT

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


флажков опций, переключателей опций, списков, полей редактирования, статиче­
ского текста, полос прокрутки и раскрывающихся меню. Все интерфейсные компо­
ненты, а также основные окна принимают события, связанные с мышью и клавиату­
рой; многие из них также поддерживают специальные события, связанные с вводом
информации пользователями. Например, элемент L i s t принимает события ItemSe-
l e c t i o n E v e n t , а S c r o l l b a r — AdjustmentEvent. В данной главе мы рассмотрим
вопросы использования основных компонентов и обработки событий.
Графическое представление управляющих элементов AWT заранее определено.
Для отображения такого элемента не надо предпринимать дополнительные действия;
метод p a i n t в данном сл)^чае не используется. Внешний вид управляющих элементов
зависит от операционной системы, в которой работает программа. Пользователь мо­
жет изменять лишь отдельные детали компонентов. С одной стороны, это упрощает
перенос программ с одной платформы на другую, а с другой стороны, разработчик
имеет ограниченный контроль над графическими объектами. Так, например, вы мо­
жете изменить надпись на кнопке, но не имеете возможности изменить форму кноп­
ки и правила ее активизации.
При необходимости разработчик может создавать собственные компоненты AWT
как подклассы класса Component. Эти компоненты будут реализованы как "легко­
весные", т.е. не будут зависеть от соответствующих средств операционной системы. Пе­
реопределив метод p a i n t , разработчик может создавать новые типы обрамления и ме­
нять цвет при отображении компонентов. Пример "легковесного" элемента, применяе­
мого для создания круга с прозрачным обрамлением, мы рассмотрим ниже.

1 3 . 1 . Класс Canvas
Компонент Canvas представляет собой простейшее окно, которое не может со­
держать другие управляющие элементы. Поскольку независимый элемент Canvas су­
ществовать не может, его обязательно надо помещать в другое окно. С Canvas нельзя
связать диспетчер компоновки. В программах объект Canvas обычно выполняет две
основные функции.
1. Создание областей рисования. Предположим, что вы создаете сложный графиче­
ский интерфейс, в котором необходима поддержка операций рисования. Вместо
того чтобы рисовать непосредственно в окне, удобнее создать для этой цели от­
дельный объект Canvas. Заметьте, что данный объект должен самостоятельно вы­
полнять операции рисования, поэтому в нем надо переопределить метод p a i n t .
2. Создание новых компонентов. Многие компоненты, определяемые разработ­
чиком, такие как статические картинки и кнопки с изображениями, создаются
на базе объектов Canvas.
В обоих случаях необходимо создавать подклассы Canvas. Непосредственно дан­
ный класс применяется достаточно редко. Выполнять рисование с помощью объекта
G r a p h i c s , полученного посредством метода g e t G r a p h i c s , не рекомендуется, по­
скольку методы u p d a t e и p a i n t класса Canvas взаимодействуют друг с другом. Вме­
сто этого внешние программы должны создавать данные, доступные Canvas, а затем,
когда необходимо выполнить рисование, вызывать метод r e p a i n t класса Canvas.
1 3 . 1 . Класс Canvas 457

М е т о д и к а профессионалов

Для того чтобы выполнять рисование в окне, необходимо переопре­


делить метод paint. Не следует получать объект Graphics другого
окна и использовать его для рисования.

Создание и использование объектов Canvas


Основные действия по созданию объектов C a n v a s показаны ниже.
Canvas c a n v a s = new C a n v a s ( ) ; / / Создание о б ъ е к т а C a n v a s ,
c a n v a s . s e t S i z e ( w i d t h , h e i g h t ) ; / / Изменение р а з м е р о в о б ъ е к т а ,
a d d ( c a n v a s ) ; / / Добавление о б ъ е к т а Canvas к к о н т е й н е р у .
/ / Для других д и с п е т ч е р о в компоновки . . .
add(canvas, r e g i o n ) ;

По умолчанию создается объект C a n v a s размером 0x0 пикселей, поэтому если вы


забудете установить размеры, данный объект отображаться не будет. Исключением из
данного правила является размещение объекта C a n v a s в области C e n t e r диспетчера
компоновки B o r d e r L a y o u t и некоторые другие случаи.
Внимание!

Не забывайте устанавливать размеры Canvas.

Пример компонента, созданного с помощью


Canvas
в листинге 13.1 приведен пример простого компонента, отображающего круг в
поле C a n v a s . Использование C a n v a s не требует модификации метода p a i n t окна,
содержащего этот объект, — вы включаете объект в окно (листинг 13.2), и Java выпол­
няет все необходимые действия. Если окно временно перекрывается другим окном,
содержимое области C a n v a s автоматически обновляется. П р и необходимости дис­
петчер компоновки родительского окна изменяет позицию C a n v a s . Результаты вы­
полнения кода показаны на рис. 13.1. В данном примере мы предоставляем объекту
A p p l e t решать, где следует располагать изображения. О размещении элементов с
помощью диспетчеров компоновки см. в главе 12.

Листинг 1 3 . 1 . C i r c l e . J a v a

import java.awt.*;
/** Компонент Circle, созданный с использованием Canvas. */

public class Circle extends Canvas {


private int width, height;

public Circle(Color foreground, int radius) {


458 Глава 13. Компоненты AWT

setForeground(foreground);
width = 2*radius;
height = 2*radius;
setSize(width, height);

public void paint(Graphics g) {


g.fillOvaKO, 0, width, height);

public void setCenter(int x, int y) {


setLocation(x - width/2, у - height/2);

Листинг 13.2.CircleTest.Java

import java.awt.*;
import Java.applet.Applet;
/** Включение трех кругов в аплет, использующий FlowLayout.

public class CircleTest extends Applet {


public void initO {
setBackground(Color.lightGray);
add(new Circle(Color.white, 30));
add(new Circle(Color.gray, 40));
add(new Circle(Color.black, 50));
}
}

Applet

Рис. 13.1. Класс Canvas можно


использовать для создания новых
Applet started.
типов графических компонентов

В AWT все графические компоненты имеют прямоугольную форму и непрозрач­


ны. Это означает, что отображаемые круги не могут перекрываться; их даже нельзя
располагать вплотную друг к другу. Код, представленный в листинге 13.3, размещает
несколько кругов по диагонали окна. Как видно из рис. 13.2, объект C a n v a s закрыва­
ют соседние круги. П р и обработке событий, например щелчков мышью, связанных с
отображаемым кругом, может возникнуть необходимость в том, чтобы щелчки за
пределами круга передавались включаемому окну. Для этого в обработчике события
придется предусмотреть специальный код, выполняющий фильтрацию событий.
1 3 . 1 . Класс Canvas 459

На заметку

В AWT все графические компоненты непрозрачны и имеют прямо-


угольную форму.

Листинг 13.3. C i r c l e T e s t : 2 . j a v a

import java.awt.*;
import Java.applet.Applet;

/** Расположение кругов по диагонали окна так, чтобы


* они соприкасались. Данный пример показывает, что
* AWT-компоненты непрозрачны и имеют прямоугольную форму.
Ч
public class CircleTest2 extends Applet {
public void initO {
setBackground(Color.lightGray);
setLayout(null); // Отключение диспетчера компоновки.
Circle circle;
int radius = getSize().width/6;
int deltaX = round(2.0 * (double)radius / Math.sqrt(2.0));
for (int x=radius; x<6*radius; x=x+deltaX) {
circle = new Circle(Color.black, radius);
add(circle);
circle.setCenter(x, x ) ;
}
}

private int round(double num) {


return((int)Math.round(num));
}

^ ^Ы Ж A ^^- Ш ^^ Ш Ш\
CircIeTest2

» .
Рис. 13.2. В AWT окна и управляющие
элементы имеют прямоугольную форму
'i^^ ••"^ф)"
и непрозрачны
460 Глава 13. Компоненты AWT

13.2. Класс Component


Все окна и управляющие элементы AWT являются "потомками" класса Component.
Ниже описаны наиболее часто используемые методы данного класса.

public v o i d a d d ( P o p u p M e n u m e n u )
public void r e m o v e ( M e n u C o m p o n e n t m e n u )
Метод a d d связывает с компонентом объект PopupMenu. Меню не отображается
на экране автоматически; для этого используется метод show класса PopupMenu.
Метод r e m o v e удаляет меню из компонента.

public v o i d addNotifyO
public void removeNotifyO
Метод a d d N o t i f y создает платформенно-ориентированный объект, связанный с
компонентом. Если для выполнения каких-либо действий надо, чтобы платфор­
менно-ориентированный объект существовал, следует вызвать данный метод либо
метод s e t V i s i b l e . П р и определении новых компонентов необходимо вызвать
метод s u p e r . a d d N o t i f y . Метод r e m o v e N o t i f y удаляет платформенно-ориенти­
рованный объект. Имена методов не совсем точно отображают выполняемые ими
действия. Назначение методов было бы более понятно, если бы они имели имена
createPeer и destroyPeer.

public void addXxxListener(X)cxListener listener)


public v o i d removeXxxListener(Xxx;Listener listener)
Данные методы задают и удаляют обработчики событий. В данном случае Ххх
означает один из следующих обработчиков: Component, F o c u s , I n p u t M e t h o d
(Java 2), Key, Mouse, M o u s e M o t i o n , P r o p e r t y C h a n g e (Java 2). Подробно об обра­
ботчиках (объектах прослушивания) см. в разделе 11.5.

public b o o l e a n contains(int х, int у)


public b o o l e a n c o n t a i n s ( P o i n t p)
Данные методы позволяют определить, расположена ли указанная точка внутри
компонента.

p r o t e c t e d final void d i s a b l e E v e n t s ( l o n g eventsToDisable)


p r o t e c t e d final void e n a b l e E v e n t s ( l o n g eventsToEnable)
Метод d i s a b l e E v e n t s запрещает, a метод e n a b l e E v e n t s разрешает передачу
указанных событий компоненту. Типы (маски) событий приведены в описании
класса AWT E v e n t .

public final void dispatchEvent(AWTEvent event)


Данный метод используется при низкоуровневой обработке событий и служит для
передачи событий компоненту.
13.2. Класс Component 461

protected void firePropertyChange(String propertyName,


Object oldValue,
Object newValue) [Java 2]
Метод f i r e P r o p e r t y C h a n g e помещает PropertyChangeEvent в очередь собы­
тий. Событие передается зарегистрированным обработчикам P r o p e r t y C h a n g e -
Listener.

public float getAlignmentXO


public float getAlignmentYO
Данные методы переопределяются для предоставления информации о выравнива­
нии. Значение O.Of указывает на то, что левая (либо верхняя) часть компонента
должна располагаться как можно ближе к оси у (либо х). Значение 0.5f соответствует
выравниванию по центру. Значение 1.Of указывает на то, что правая (либо нижняя)
часть компонента должна располагаться как можно дальше от оси у (либо х). По
умолчанию для вертикального и горизонтального выравнивания используется вели­
чина 0.5f Для обозначения типов выравнивания в классе Component определены
следующие константы: TOP_ALIGNMENT (O.Of), CENTER_ALIGNMENT (0.5f),
BOTTOM_ALIGNMENT (l.Of), LEFT_ALIGNMENT (O.Of) и RIGHT_ALIGNMENT (l.Of).

public Color getBackgroundO


public void setBackground(Color bgColor)
Метод getBackground возвращает, a метод setBackground устанавливает цвет
фона компонента.

public Rectangle getBounds()


public void setBounds(int x, int y, int width, int height)
public void setBounds(Rectangle boundingRectangle)
Метод getBounds возвращает объект Rectangle, описывающий границы объекта.
R e c t a n g l e — это неграфическая структура данных, содержащая поля х, у, width и
h e i g h t . Два варианта метода setBounds изменяют как размеры, так и расположе­
ние компонента. Метод setBounds заменяет методы s e t S i z e и s e t L o c a t i o n .

public Component getComponentAt(int x, int y)


public Component getComponentAt(Point p)
Данные методы возвращают ссылку на самый "верхний" элемент в указанной по­
зиции. Если в данной позиции нет вложенных компонентов, возвращается тот
компонент, которому принадлежит метод. Если указанные координаты лежат за
пределами компонента, возвращается значение n u l l .

public Cursor getCursor()


public void setCursor(Cursor cursor)
Метод g e t C u r s o r возвращает текущий курсор. Метод s e t C u r s o r используется в
любом компоненте для отображения курсора. Чтобы получить объект Cursor, на-
462 Глава 13. Компоненты AWT

до вызвать метод C u r s o r . g e t P r e d e f i n e d C u r s o r ( t y p e ) , где в качестве атрибута


type задается одна из констант, определенных в классе Cursor:
CROSSHAIR_CURSOR, DEFAULT_CURSOR, E_RESIZE_CURSOR, HAND_CURSOR,
MOVE_CURSOR, N_RESIZE_CURSOR, NE_RESIZE_CURSOR, NW_RESIZE_CURSOR,
S_RESIZE_CURSOR, SE_RESIZE_CURSOR, SW_RESIZE_CURSOR, TEXT_CURSOR,
W_RESIZE_CURSORилиWAIT_CURSOR.

public Font getFont()


public void s e t F o n t ( F o n t f)
Метод g e t F o n t возвращает текущий шрифт, установленный с помощью метода
s e t F o n t либо унаследованный от включающего окна. Объект G r a p h i c s , исполь­
зуемый методами p a i n t и u p d a t e , также наследует ш р и ф т компонента.

public F o n t M e t r i c s g e t F o n t M e t r i c s ( F o n t f)
Метод g e t F o n t M e t r i c s возвращает объект F o n t M e t r i c s , созданный на базе за­
данного шрифта. Объект F o n t M e t r i c s используется для определения ширины и
высоты символов или строк, отображаемых данным шрифтом.

public Color g e t F o r e g r o u n d ( )
public void s e t F o r e g r o u n d ( C o l o r fgColor)
Данные методы определяют и устанавливают цвет переднего плана для компонен­
та. Объект G r a p h i c s наследует цвет переднего плана компонента.

public Graphics getGraphics()


Метод g e t G r a p h i c s возвращает объект G r a p h i c s , который может применяться
для рисования в области, занимаемой компонентом. П р и инициализации объекта
G r a p h i c s используется шрифт, цвет переднего плана и фона компонента.

public P o i n t getLocationO
public void setLocation(int x, int y)
public void s e t L o c a t i o n ( F o i n t p)
Метод g e t L o c a t i o n возвращает объект P o i n t (содержащий поля x и у), который
указывает расположение верхнего левого угла компонента в системе координат
включающего окна. Для получения абсолютных координат используется метод
g e t L o c a t i o n O n S c r e e n . Варианты метода s e t L o c a t i o n перемещают верхний ле­
вый угол компонента в позицию, заданную в системе координат включающего ок­
на. Если включающее окно связано с диспетчером компоновки, диспетчер отменя­
ет установленную позицию компонента.

public Point getLocationOnScreen()


Данный метод возвращает абсолютные координаты левого верхнего угла компо­
нента. Исключение составляет аплет. Получить его абсолютные координаты не­
возможно.
13.2. Класс Component 463

public Dimension getMinimumSize()


p u b l i c D i m e n s i o n getMaximumSize()
p u b l i c D i m e n s i o n getPreferredSizeO
Данные методы возвращают объект D i m e n s i o n (содержащий поля х, у, w i d t h и
h e i g h t ) , который описывает наименьший, наибольший и нормальный размеры
компонента. Эти значения используются диспетчером компоновки, который мо­
жет изменять размеры компонента лишь в допустимых пределах.

p u b l i c Container getParent()
Метод g e t P a r e n t возвращает включающее окно (если объект Frame или
Component не связан с окном, возвращается значение n u l l ) .

p u b l i c D i m e n s i o n getSize()
public void setSize(int w i d t h , int height)
p u b l i c void s e t S i z e ( D i m e n s i o n d)
Метод g e t S i z e возвращает текущие размеры компонента в виде объекта Dimension,
содержащего поля w i d t h и h e i g h t . Метод s e t S i z e изменяет размеры компонента,
вызывая s e t B o u n d s . При вызове методу s e t B o u n d s передаются заданные ширина и
высота, а также текущие координаты верхнего левого угла компонента.

p u b l i c Toolkit getToolkit()
Данный метод возвращает объект T o o l k i t , который используется для загрузки изо­
бражений ( g e t l m a g e ) , поиска доступных шрифтов ( g e t F o n t L i s t ) , определения раз­
меров экрана ( g e t S c r e e n S i z e ) , его разрешения ( g e t S c r e e n R e s o l u t i o n ) и т.д.

p u b l i c void invalidateO
p u b l i c void validateO
Метод i n v a l i d a t e устанавливает внутренний флаг, указывающий на то, что дан­
ный компонент и все вложенные компоненты некорректны и перед выводом не­
обходимо проверить их расположение. Метод v a l i d a t e используется совместно с
классом C o n t a i n e r и предназначен для обновления размещения данного компо­
нента и всех вложенных компонентов.

p u b l i c b o o l e a n isEnabled()
Метод i s E n a b l e d определяет, доступен ли данный компонент (см. описание ме­
тода s e t E n a b l e d ) .

p u b l i c b o o l e a n isFocusTraversable()
Метод i s F o c u s T r a v e r s a b l e указывает, может ли компонент получить фокус по­
сле нажатия пользователем клавиши <ТаЬ> или комбинации клавиш <Shift+Tab>.
Если метод возвращает значение f a l s e , компонент, тем не менее, может явно за­
просить фокус с помощью метода r e q u e s t F o c u s .
464 Глава 1 3 . Компоненты AWT

public boolean isShowingO


public boolean isVisible()
Метод i s Showing определяет, является ли компонент видимым и отображается
ли окно, которое содержит данный компонент. Метод i s V i s i b l e указывает, яв­
ляется ли компонент видимым. Даже если метод i s V i s i b l e возвращает значение
t r u e , компонент не обязательно будет отображаться на экране; это зависит от то­
го, видимо ли включающее окно.

public boolean isValid()


Метод i s V a l i d позволяет определить, корректен ли компонент (см. описание ме­
тодов v a l i d a t e и i n v a l i d a t e ) .

public void list()


public void list(PrintStream stream)
public void list(PrintStream stream, int indentation)
public void list(PrintWriter writer)
public void list(PrintWriter writer, int indentation)
Данные методы выводят информацию о родительских и подчиненных компонентах,
выполняя просмотр дерева. При выводе информация о включаемых компонентах
сдвигается относительно родительских компонентов. Данные методы полезны в
процессе разработки программ, поскольку, вызвав метод l i s t для окна верхнего
уровня, можно получить информацию обо всех компонентах приложения.

М е т о д и к а профессионалов

Метод list может оказать существенную помощь при отладке про­


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

public void paint(Graphics g)


Метод p a i n t вызывается в ответ на обращение к методу r e p a i n t либо после того,
как компонент временно исчезает из поля зрения. Для выполнения графических
операций надо переопределить p a i n t . Если из поля зрения исчезает лишь часть
площади компонента, устанавливается область отсечения объекта G r a p h i c s , со­
ответствующая этой части.

public void paintAll(Graphics g)


Данный метод отображает как текущий компонент, так и все вложенные компо­
ненты.
13.2. Класс Component 465

public boolean preparelmage(lmage image,


ImageObserver observer)
public boolean preparelmage(lmage image, int width,
int height,
ImageObserver observer)
Данный метод начинает загрузку данных, соответствующих объекту Image. Они
используются для предварительной загрузки изображений перед выводом.
(Подробно загрузка изображений была рассмотрена в главе 9.) Заметьте, что мас­
штабированные изображения рассматриваются как новые, поэтому если вам надо
вывести изображение, размеры которого отличаются от заданных по умолчанию,
надо вызвать метод p r e p a r e Image, указав требуемые размеры. Если данные, соот­
ветствующие изображению, доступны, возвращается значение t r u e , в противном
случае возвращается f a l s e .

public void print(Graphics g)


public void printAll(Graphics g)
Если объект G r a p h i c s реализует интерфейс P r i n t G r a p h i c s , данные методы вы­
водят компонент. Реализация этих методов по умолчанию не выполняет никаких
действий, кроме вызова p a i n t и p a i n t A l l .

public void processXjcxEvent(Xx3cEvent event)


Данный метод обрабатывает связанные низко)ровневые события (см. описание
e n a b l e E v e n t s ) . Последовательность символов Ххл: заменяется одним из следую­
щих типов событий: Component, Focus, InputMethod (Java 2) Key, Mouse или
MouseMotion. Подробно о событиях см. в главе И.

public void repaintO


public void repaint(int x, int y, int width, int height)
public void repaint(long milliseconds)
public void repaint(long milliseconds, int x, int y, int width,
int height)
Данные методы указывают потоку AWT на то, что метод u p d a t e должен быть вы­
зван при первой возможности. Объект P a i n t E v e n t помещается в очередь собы­
тий. По достижении начала очереди P a i n t E v e n t передается методу u p d a t e , в ре­
зультате чего экран очищается, а затем вызывается метод p a i n t . Если задана пря­
моугольная область, она используется в качестве области отсечения.

public void requestFocusO


Данный метод вызывается при получении компонентом фокуса ввода. Например,
вы можете использовать r e q u e s t Focus так, что щелчок мышью на соответствую­
щей кнопке (компонент Button) приведет к получению фокуса компонентом
TextField.
466 Глава 13. Компоненты AWT

public v o i d s e t £ n a b l e d ( b o o l e a n enabledFlag)
Метод s e t E n a b l e d делает AWT-компонент доступным ( t r u e ) или запрещает дос­
туп к нему ( f a l s e ) . В большинстве операционных систем запрет управляющего
элемента приводит к тому, что оно отображается затененным на экране.

public v o i d setVisible(boolean visibleFlag)


Метод s e t V i s i b l e делает AWT-компонент видимым ( t r u e ) или невидимым
(false).

public void transferFocus()


Данный метод передает фокус следующему элементу так же, как это происходит
после нажатия клавиши <ТаЬ>. Порядок следования элементов определяется по­
следовательностью их включения в окно.

public void u p d a t e ( G r a p h i c s g)
Данный метод вызывается потоком передачи событий AWT после вызова метода
r e p a i n t . П о умолчанию при этом очищается экран, а затем вызывается метод
p a i n t ( g ) . Часто u p d a t e переопределяется так, чтобы перед вызовом p a i n t эк­
ран не очищался. Данный подход типичен для рисования вне экрана с помощью
карты пикселей. Более детально рисование вне экрана мы обсудим в главе 16 при
рассмотрении двойной буферизации.

13.3. "Легковесные" компоненты в Java 1.1


В Java 1.1 были созданы "легковесные" компоненты, представляющие собой непо­
средственные подклассы класса Component, не связанные с оконной системой кон­
кретной платформы. Все области, за исключением нарисованных в методе p a i n t ,
отображаются так, что сквозь них видны нижележащие компоненты. В листинге 13.4
показан класс B e t t e r C i r c l e , в котором используется данный подход. Код класса
аналогичен варианту с использованием C a n v a s (листинг 13.1), за исключением того,
что в данном случае мы переопределяем методы g e t P r e f e r r e d S i z e и g e t M i n i m u m -
S i z e , чтобы они возвращали текущий размер. В общем случае эти два метода должны
использоваться для вычисления оптимального и минимального размеров компонен­
та. В листинге 13.5 показан код аплета, использующий B e t t e r C i r c l e так же, как ап-
лет, рассмотренный в начале данной главы, использовал класс C i r c l e . Как видно из
рис. 13.3, перекрывающиеся углы больше не заслоняют круги, нарисованные на со­
седних компонентах.
П р и использовании "легковесных" компонентов в контейнере с переопределен­
ным методом p a i n t в этом методе должен присутствовать вызов s u p e r . p a i n t , в
противном случае "легковесные" компоненты отображаться не будут.
13.3. "Легковесные" компоненты в Java 1.1 467

Листинг 1 3 . 4 . B e t t e r C i r c l e . J a v a

import java.awt.*;
/•• Модифицированный вариант класса Circle, использующий
* вместо Canvas "легковесные" компоненты Java 1.1.

public class BetterCircle extends Component {


private Dimension preferredDimension;
private int width, heights-
public BetterCircle(Color foreground, int radius) {
setForeground(foreground);
width = 2*radius;
height = 2*radius;
preferredDimension = new Dimension(width, height);
setSize(preferredDimension);
}
public void paint(Graphics g) {
g.setColor(getForeground());
g.fillOval(0, 0, width, height);
}
public void setCenter(int x, int y) {
setLocation(x - width/2, у - height/2);
}
/** Возвращает исходный размер, в результате чего
* размеры BetterCircle не уменьшаются после
* действий диспетчера компоновки.
*/

public Dimension getPreferredSize() {


return(preferredDimension);
}
/** Исходные размеры возвращаются как минимальные
V
public Dimension getMinimumSize() {
return(preferredDimension);
468 Глава 13. Компоненты AWT

Листинг 13.5. BetterCircleTest.Java

import java.awt.*;
import java.applet.Applet;

/** Круги располагаются по диагонали, причем


* соприкасаются друг с другом. Данн1э1Й пример lightweight
* демонстрирует частичную прозрачность "легковесных"
* компонентов Java 1.1.

public class BetterCircleTest extends Applet {


public void initO {
setBackground(Color.lightGray) ;
setLayout(null) ;
BetterCircle circle;
int radius = getSize().width/6;
int deltaX = round(2.0 * (double)radius / Math.sqrt(2.0));
for (int x=radius; x<6*radius; x=x+deltaX) {
circle = new BetterCircle(Color.black, radius);
add(circle);
circle.setCenter(x, x ) ;
}
}
private int round(double num) {
return((int)Math.round(num));
}

;£ie £dt> lifiew £0 !^tmMvu*ot Н<Ф

BetterCircleTest

Рис. 13.3. В Java 1.1 "легковесные"


gjj»<i»jj>r
компоненты могут быть прозрачными
13.4. Класс Panel 469

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


управляющие элементы, внешний вид которых не изменяется в зависимости от плат­
формы. Практически все компоненты Swing являются легковесными и не используют
при рисовании платформенно-зависимые элементы. "Легковесные" Swing-компонен­
ты мы рассмотрим в главе 14.

13.4. Класс Panel


P a n e l представляет собой окно без обрамления, которое может содержать в себе
управляющие элементы и вложенные окна. Подобно C a n v a s , P a n e l не может ис­
пользоваться как независимое окно; эти компоненты надо помещать в существующие
окна. В Java-программах P a n e l обычно выполняет следующие функции.
1. Группировка компонентов. Создавая сложные интерфейсы, вы можете разде­
лить экран на несколько прямоугольных областей, о ф о р м и в каждую из них как
объект P a n e l . Панели отвечают за размещение в них элементов, а в родитель­
ском окне надо лишь поместить панели в требуемые позиции.
2. Создание новых компонентов, в которые должны включаться другие компо­
ненты.

Диспетчер компоновки по умолчанию: FlowLayout


Окна, которые могут содержать другие компоненты (т.е. контейнеры), используют
для автоматического размещения элементов диспетчеры компоновки ( L a y o u t M a n a -
g e r ) . В различных операционных системах компоненты имеют разные размеры. П р и
изменении размеров окна, а также при добавлении или удалении компонентов дис­
петчер компоновки автоматически вычисляет позиции компонентов.
Диспетчер F l o w L a y o u t сохраняет нормальные размеры компонентов и распола­
гает их строками слева направо, выравнивая по левому, правому краям или по центру.
Более того, размеры самой панели выбираются так, чтобы в них можно было размес­
тить все необходимые компоненты. Таким образом, если вы поместите P a n e l в дру­
гое окно, панель может быть растянута либо сжата. В частности, если вы поместите
пустой элемент P a n e l в контейнер, с которым связан диспетчер компоновки
F l o w L a y o u t , ширина и высота панели будут установлены равными нулю и панель не
будет отображаться.

Внимание!
Если вы поместите пустой элемент Panel в контейнер, с которым ^
связан диспетчер компоновки FlowLayout, панель не будет видна на
экране.
470 Глава 13. Компоненты AWT

Создание и использование панелей


в последующих разделах мы опишем основные действия по созданию и использова­
нию объектов P a n e l . Поскольку нормальный размер P a n e l вычисляется на основании
включенных компонентов, размер панели считается не определенным до тех пор, пока
она не будет включена в контейнер. Действия по созданию панели приведены ниже.
// Создание объекта Panel.
Panel panel = new Panel();
// Добавление компонентов в панель.
panel.add(someComponent);
panel.add(someOtherComponent);
// Добавлении панели в контейнер.
// При использовании FlowLayout:
container.add(panel) ;
// При использовании BorderLayout:
container.add(panel, region);

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


компонентов
Рассмотрим программы, демонстрирующие использование панели для группиров­
ки кнопок. В первом примере (листинг 13.6) в аплет включаются восемь кнопок. Как
видно на рис. 13.4, первые шесть кнопок размещаются в первой строке, а оставшиеся
две — во второй строке. Такое размещение нельзя назвать идеальным, поскольку пер­
вые четыре кнопки логически связаны между собой и принадлежат одной группе, а
остальные четыре образуют другую группу. Во втором примере (листинг 13.7) каждый
из наборов кнопок объединяется в отдельной панели. Результаты выполнения кода
показаны на рис. 13.5.

Листинг 1 3 . 6 . B u t t o n T e s t l . j a v a

import Java.applet.Applet;
import java.awt.'^;
/ * * В окно а п л е т а , использующего д и с п е т ч е р компоновки
* F l o w L a y o u t , включаются восемь кнопок, не объединенных
* в группы.
V
public class ButtonTestl extends Applet {
public void init() {
String[] labelPrefixes = { "Start", "Stop", "Pause",
"Resume" };
for (int i=0; i<4; i++) {
add(new Button(labelPrefixes[i] + " Threadl"));
}
for (int 1=0; i<4; i++) {
add(new Button(labelPrefixes[i] + " Thread2"));
}
}
}
13.4. Класс Panel 471

K3S3|
• Fte £cW V)«rt4 Favortfes Joote HeJp

^^^ - J J 'J a ^ J -Л' _* -^ • J


Jl
ButtonTestl
Sfaft Threadi 1 StopThreadl | Pause Threadl | Reswio Th«ad1 | Slet ТГшхй | 3^«рТНимйй|

Pause Th»ftad2 | Resume Ttyeacgj

^ Done f^i My Conputef


A
Рис. 13.4. Без применения вложенных панелей компоненты рас­
полагаются построчно в таком же порядке, в каком они включаются в
контейнер

Листинг 13.7.ButtonTest2.java

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
/** В каждую из панелей включаются четыре кнопки. */
p u b l i c c l a s s ButtonTest2 extends Applet {
p u b l i c void i n i t O {
S t r i n g [ ] l a b e l P r e f i x e s = { " S t a r t " , "Stop", "Pause",
"Resume" };
Panel p i = new P a n e l ( ) ;
for ( i n t i=0; i<4; i++) {
pi.add(new B u t t o n ( l a b e l P r e f i x e s [ i ] + " T h r e a d l " ) ) ;
}
Panel p2 = new P a n e l ( ) ;
for ( i n t i=0; i<4; i++) {
p2.add(new B u t t o n ( l a b e l P r e f i x e s [ i ] + " Thread2"));
}
add(pi);
add(p2);

Ftte £ck View Favof^es Ttxrfs Ь|ф


-'

ButtonTest2
j Staff Threadl j StopThreadl | Paute Threadl j Resume Threadl |

Start Thread^ | Stop Thfead2 | Раиге TNead2 | Retufw Tbead2 j

. 0 j АррЫ statted ..J Щ€ощ)и-гя

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


472 Глава 13. Компоненты AWT

13.5. Класс Container


В предыдущем разделе мы рассмотрели объект P a n e l ~ основное окно Java, пред­
назначенное для размещения других компонентов. Окна, которые могут содержать
другие компоненты, принадлежат классу C o n t a i n e r . Класс C o n t a i n e r является под­
классом класса Component и наследует его методы (они были рассмотрены выше в
данной главе). Кроме того, в классе C o n t a i n e r определены следующие дополнитель­
ные методы.

public C o m p o n e n t a d d ( C o m p o n e n t с)
public C o m p o n e n t a d d ( C o m p o n e n t с, Object constraints)
Первый из указанных методов помещает компонент в последнюю позицию масси­
ва компонентов и используется со всеми стандартными диспетчерами компонов­
ки. Единственным исключением является B o r d e r L a y o u t , где необходимо явно
задавать область B o r d e r L a y o u t .NORTH, B o r d e r L a y o u t . SOUTH, B o r d e r L a y o u t .
EAST, B o r d e r L a y o u t . WEST или B o r d e r L a y o u t .CENTER. Помимо двух приведен­
ных методов a d d , класс Component содержит методы, позволяющие при включе­
нии нового компонента задавать индекс в массиве компонентов. Подробнее эти
вопросы рассмотрены в разделе документации по API, посвященном j a v a . a w t .
Container.

public void a d d C o n t a i n e r L i s t e n e r ( C o n t a i n e r L i s t e n e r listener)


public void r e m o v e C o n t a i n e r L i s t e n e r ( C o n t a i n e r L i s t e n e r listener)
Эти методы предназначены для добавления обработчиков событий, связанных с
контейнером, и удаления обработчиков. П р и включении компонента в контейнер
или при его удалении генерируется событие C o n t a i n e r E v e n t , которое передает­
ся обработчику ( C o n t a i n e r L i s t e n e r ) .

public int g e t C o m p o n e n t C o u n t O
Данный метод возвращает число компонентов, содержащихся в контейнере. Под-
считываются все компоненты, в том числе невидимые (см. описание метода
i s V i s i b l e класса C o m p o n e n t ) .

public C o m p o n e n t g e t C o m p o n e n t ( i n t position)
Метод g e t Component возвращает N-й элемент, содержащийся в контейнере
(окне). Номер О соответствует первому из включенных компонентов. Если вы за­
дадите номер позиции больший или равный значению, возвращаемому методом
g e t C o m p o n e n t C o u n t , будет сгенерировано исключение A r r a y l n d e x O u t O f B o -
undsException.

public C o m p o n e n t [ ] g e t C o m p o n e n t s ( )
Данный метод возвращает массив компонентов, содержащихся в контейнере
(окне). Массив может иметь нулевую длину; значение n u l l никогда не возвраща­
ется вместо массива.
13.5. Класс Container 473

public LayoutManager getLayout()


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

public Insets getlnsetsO


Метод g e t l n s e t s возвращает объект I n s e t s (содержащий поля t o p , bottom,
l e f t и r i g h t ) , который описывает границы окна. Метод s e t l n s e t s недоступен
разработчику. Вы можете переопределить метод g e t l n s e t s так, чтобы он воз­
вращал значение, отличное от значения по умолчанию (определяемое платфор-
менно-зависимыми окнами), либо получить объект I n s e t s и модифицировать его
поля, объявленные как p u b l i c .

public boolean isAncestorOf(Component possibleSubComponent)


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

public void paint(Graphics g)


public void print(Graphics g)
Данные методы унаследованы от класса Component. Однако использование их в
контейнере существенно отличается от использования в компоненте. Если в ва­
шем окне содержится один или несколько "легковесных" компонентов и вы пере­
определите метод p a i n t или p r i n t , то необходимо вызвать s u p e r . p a i n t или
s u p e r . p r i n t , чтобы компоненты отображались корректно. Поскольку вы нико­
гда не знаете, будут ли впоследствии включены "легковесные" компоненты, жела­
тельно всегда в конце метода p a i n t или p r i n t вызывать соответствующий метод
суперкласса.

public void processContainerEvent(ContainerEvent event)


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

public void remove(int Component)


public void remove(int position)
Метод remove удаляет указанный компонент из окна. Вы также можете удалить N-
й элемент массива компонентов. Индекс О соответствует первому из добавленных
компонентов, а значение getComponentCount () ~1 — последнему компоненту.

public void setLayout(LayoutManager manager)


Данный метод заменяет диспетчер компоновки на новый. Так, например, вы мо­
жете заменить в Frame BorderLayout на FlowLayout либо сообщить объекту
Applet о том, что он должен использовать диспетчер FlowLayout с расстоянием
между компонентами 10 пикселей вместо 5, заданных по умолчанию.
474 Глава 13. Компоненты AWT

13.6. Класс Applet


Класс A p p l e t чаще всего используется в Java-программах, встраиваемых в Web-
страницы. Данный класс можно применять и при построении Java-приложений, но в
этом случае гораздо удобнее использовать класс P a n e l , который является суперклассом
класса A p p l e t . С классом A p p l e t по умолчанию связан тот же диспетчер компоновки,
что и с P a n e l , — F l o w L a y o u t . Подробнее об использовании A p p l e t см. в главе 9.

13.7. Класс ScrollPane


S c r o l l P a n e — это окно без обрамления и диспетчера компоновки, используемое в
тех случаях, когда объем содержимого окна велик и все компоненты невозможно ото­
бразить одновременно. В окне S c r o l l P a n e может находиться только один компо­
нент — объект P a n e l , содержащий другие компоненты.

Создание и использование ScrollPane


Несмотря на то что объект S c r o l l P a n e предоставляет некоторые средства для
управления его содержимым, чаще разработчики помещают в окно S c r o l l P a n e ком­
понент или контейнер большого размера и предоставляют пользователю просматри­
вать его с помощью полос прокрутки. Создавая компонент S c r o l l P a n e , вы можете
задать один из трех вариантов работы с полосами прокрутки: SCROLLBARS_ALWAYS,
SCROLLBARS_AS_NEEDED, SCROLLBARS_NEVER. В объекте S c r o l l P a n e , созданном с
помощью конструктора по умолчанию, полосы прокрутки используются только тогда,
когда они необходимы. Ниже приведен фрагмент кода, демонстрирующий основные
действия по созданию и использованию S c r o l l b a r .
/ / Создание S c r o l l P a n e , демонстрирующего наличие
/ / полос п р о к р у т к и .
S c r o l l P a n e pane =
new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS);
/ / Установка р а з м е р о в компонента S c r o l l P a n e и
/ / включение е г о в к о н т е й н е р ,
рапе.setSize(width, height);
add(pane);

Пример объекта ScrollPane, включающего


панель, которая содержит 100 кнопок
в листинге 13.8 показан код, в котором используется простой объект S c r o l l P a n e .
В него включен объект P a n e l , содержащий 100 кнопок. На рис. 13.6 показан резуль­
тат выполнения кода после того, как ползунок на горизонтальной полосе прокрутки
был передвинут вправо.
13.8. Класс Frame 475

Листинг 13.8.ScrollPaneTest-Java

import Java.applet.Applet;
import j ava.awt.*;

/*^ Панель, содержащая 100 кнопок, включается в состав


* ScrollPane. Размеры ScrollPane недостаточны, чтобы
* одновременно отобразить все кнопки.
Ч
public class ScrollPaneTest extends Applet {
public void initO {
setLayout(new BorderLayout());
ScrollPane pane = new ScrollPane();
Panel bigPanel = new PanelO;
bigPanel.setLayout(new GridLayout(10, 10));
for(int i=0; i<100; i++) {
bigPanel.add(new Button("Button " + i));
}
pane.add(bigPanel);
add(pane, BorderLayout.CENTER);
}

|.^^.f-ui»;i!^u'i''iJii.^ffViffMPf?TWT^''^^^
Apptet

1 виОоп2 еиШ)пЗ. Button 4 Buttons Sue


1 Button 12 Button 13 Bulton14 BtritonlS ButtJ

1 Button 22 Sutton 23 Sutton 24 Button 25 ButtJ

1 Button 32 Button 33 Sutton 34 Bu№n36 Butt*

1 Button 42 Button 43 8u№n44 Button 46 Butt»

1 Button $2 9Ш0П53 Sutton S4 Button 56 Butt.

1 Button $2 Button S3 Sutton M В»«5П66 Butt*

1 Button 72 » Л 0 П 7 3 Suttdn74 Button 76 Btitt*

1 Button $2 Button 83 Sulton?4 Button €6 Butt*

1 Button 92 Button 93 Sutt8n94 Button 96 Butt* Р1ЛС. 13.6. Окно S c r o l l P a n e можно прокручивать,
Lu ^ ^1 отображая части включенного компонента, лежащие
Applet started. 1 за пределами окна

13.8. Класс Frame


Класс Frame играет роль базовых средств при создании графических приложений.
Кроме того, он используется для создания раскрывающихся окон в аплете. Чаще все­
го объект Frame используют для создания независимых окон. В состав Frame входит
заголовок, строка меню, обрамление, курсор и пиктограмма. Компонент Frame явля­
ется контейнером и может содержать другие элементы графического интерфейса.
476 Глава 13. Компоненты AWT

Диспетчер компоновки по умолчанию:


BorderLayout
Диспетчер компоновки B o r d e r L a y o u t разбивает окно на пять областей: NORTH,
SOUTH, EAST, WEST и CENTER. Каждая область может содержать как минимум один
компонент; добавляя компонент в окно, необходимо указывать область, в которую он
должен быть помещен. Компонент, включаемый в область NORTH (SOUTH), располага­
ется в верхней (нижней) части окна. Высота компонента сохраняется неизменной
(см. описание метода p r e f e r r e d S i z e класса C o m p o n e n t ) , а по горизонтали компо­
нент растягивается так, чтобы его ширина была равна ширине включающего окна, за
исключением установленных границ (см. описание метода g e t l n s e t s класса
C o n t a i n e r ) . Компонент, включаемый в область EAST (WEST), располагается в правой
(левой) части окна; ширина компонента остается неизменной, а высота изменяется
так, чтобы она совпадала с высотой окна за исключением границ и высоты занятых
областей NORTH и SOUTH. Оставшееся пространство в окне выделяется под область
CENTER. Если вы имеете опыт создания аплетов, но мало знакомы с классом Frame, у
вас могут возникнуть трудности при работе с диспетчером компоновки. Для того что­
бы отказаться от B o r d e r L a y o u t и связать с окном более знакомый диспетчер
F l o w L a y o u t , следует использовать метод s e t L a y o u t . Подробно о диспетчере ком­
поновки B o r d e r L a y o u t см. в главе 12.

Создание и использование объектов Frame


Создавая объект Frame, вам надо решить, следует ли явно задавать размеры этого
компонента, используя для этого s e t S i z e , либо надо, чтобы размер Frame опреде­
лялся размерами содержащихся в нем компонентов. Ниже приведены фрагменты
программы, иллюстрирующие оба подхода.
Для создания Frame фиксированного размера используется следующий код:
/ / Создание о б ъ е к т а Frame.
Frame frame = new F r a m e ( t i t l e S t r i n g ) ;
/ / Добавление к о м п о н е н т о в .
f r a m e . a d d ( s o m e P a n e l , BorderLayout.CENTER);
f r a m e . a d d ( o t h e r P a n e l , BorderLayout.NORTH);

/ / Установка р а з м е р о в Frame. Компонент р а с п о л а г а е т с я в


/ / верхнем левом у г л у э к р а н а .
frame.setSize(width, height);
/ / В противном с л у ч а е надо з а д а в а т ь и р а з м е р , и позицию.
frame.setBounds(left, top, width, h e i g h t ) ;
/ / Отображение Frame.
frame.setVisible(true) ;
Разработчики, не имеющие достаточного опыта работы с окнами Frame, часто по­
вторяют одну и ту же ошибку— забывают указать область, в которую должен вклю­
чаться компонент. Если вы используете вызов f r a m e . a d d ( s o m e C o m p o n e n t ) , указан­
ный компонент будет размещен в области CENTER. Каждый последующий вызов
( s o m e O t h e r C o m p o n e n t ) будет заменять компонент, находящийся в области CENTER,
новым компонентом.
13.8- Класс Frame 477

Для того чтобы создать окно Frame, размеры которого определяются размерами
компонентов, содержащихся в окне, надо написать фрагмент кода, подобный приве­
денному ниже.
/ / Создание объекта Frame.
Frame frame = new F r a m e ( t i t l e S t r i n g ) ;
/ / Позиционирование компонента Frame (необязательное действие).
frame.setLocation(left, top);
/ / Добавление компонентов.
frame.add(somePanel, BorderLayout.CENTER);
/ / Растягивание компонента Frame.
frame.packO ;
/ / Отображение компонента Frame,
frame.setVisible(true);
Вызывая метод pack, вы создаете платформенно-зависимый компонент Frame
(см. описание метода addNotif у класса Component), и диспетчер компоновки распо­
лагает все компоненты в контейнере. Размеры окна не превышают необходимые для
размещения компонентов.
Если вы хотите запретить пользователю изменять размеры Frame, вызовите метод
s e t R e s i z e a b l e ( f a l s e ) . Определить размеры экрана перед позиционированием
Frame можно, вызвав метод g e t S c r e e n S i z e объекта, возвращаемого методом
g e t T o o l K i t . Следующий фрагмент кода располагает окно Frame по центру экрана.
Toolkit t o o l k i t = frame.getToolkit();
Dimension s c r n S i z e = t o o l k i t . g e t S c r e e n S i z e ( ) ;
Dimension frameSize = f r a m e . g e t S i z e ( ) ;
frame.setLocation((scrnSize.width-frameSize.width)/2,
(scrnSize.height-frameSize.height)/2);
Аплеты также могут генерировать окна на базе Frame, однако они обычно поме­
чаются специальными сообщениями "Unsigned Java Applet Window" — при рабо­
те с Netscape (рис. 13.7) или "Warning: Applet Window" (при работе с Internet
Explorer). Это затрудняет работу с раскрывающимися окнами в аплетах.

Рис. 13.7. Окна на базе Frame, используемые в аплетах,


обычно выводят в строке состояния предупреждающие
сообщения

Примеры использования объектов Frame


Ниже представлены два примера создания объектов Frame. В первом примере,
приведенном в листинге 13.9, все необходимые действия по созданию Frame выпол­
няются в методе main. Обычно в первую очередь создается экземпляр класса Frame
(или экземпляр одного из подклассов данного класса), и ссылка на него присваивает­
ся переменной. Все необходимые изменения (установка размеров окна, добавление
478 Глава 13. Компоненты AWT

компонентов) выполняются в теле метода m a i n с помощью данной переменной. Та­


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

Листинг 1 3 . 9 . F r a m e E x a m p l e l . J a v a

import java.awt.*;
p u b l i c c l a s s FrameExamplel {
public s t a t i c void main(String[] args) {
Frame f = new F r a m e ( " F r a m e Example 1")
f.setSize(400, 300);
f.setVisible(true);
}
} '

Bo втором примере, код которого приведен в листинге 13.10, действия по созда­


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

Листинг 1 3 . 1 0 . F r a m e E x a m p l e 2 . j a v a

import java.awt.*;

public class FrameExample2 extends Frame


public static void main(String[] args)
new FrameExample2();
}
public FrameExample2() {
superC'Frame Example 2 " ) ;
setSize(400, 300);
setVisible(true);
}
13.8. Класс Frame 479

Закрытие окон Frame


П о умолчанию объект Frame игнорирует все события, в том числе связанные с
действиями по закрытию окна. Для того чтобы окно могло быть корректно закрыто,
необходимо разрешить обработку низкоуровневых событий либо связать с Frame об­
работчик WindowListener. В листинге 13.11 показан код класса CloseableFrame, в
котором при попытке пользователя закрыть окно вызывается метод System.exit.
На основе этого класса далее в этой главе мы будем создавать графическое приложе­
ние. Объекты Frame, которые создаются из существующих окон (аплетов или других
окон Frame), в ответ на действия пользователя по закрытию окна должны вместо
System, exit вызывать dispose. Поскольку заголовок является частью окна, не­
сколько пикселей остаются невидимыми. Поэтому при рисовании в окнах Frame вы­
зывайте метод getlnsets, чтобы определить место, доступное для рисования, либо
включайте другой контейнер (например, Panel) в область CENTER окна Frame и вы­
полняйте операции рисования в этом контейнере.

Листинг 13.11.CloseableFrame.Java

import java.awt.*;
import Java.awt.event.*;

/** Объект Frame, допускающий закрытие окна, используется


•*^ в качестве базового объекта для создания
* графических приложений Java 1.1.

public class CloseableFrame extends Frame {


public CloseableFrame(String title) {
super(title);
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
}
/•• в теле обработчика события надо вызвать обработчик
* суперкласса (super.processWindowEvent).
V
public void processWindowEvent(WindowEvent event) {
super.processWindowEvent(event); // Поддержка обработчиков,
if (event.getlDO == WindowEvent.WINDOW_CLOSING) {
// Если объект Frame используется в составе аплета,
// надо вызывать dispose().
System.exit(0);
}
}
480 Глава 13. Компоненты AWT

Меню
Окно Frame позволят использовать строку меню, содержащую одно или несколько
раскрывающихся меню. Для работы с меню надо сначала создать объект MenuBar, а
затем один или несколько объектов Menu. В объект Menu можно включать объект
S t r i n g , другой объект Menu (для организации каскадных меню) либо объект
C h e c k b o x M e n u I t e m (для выбора пунктов с помощью флажков опций). Созданные
меню надо включить в MenuBar с помощью метода add, принадлежащего MenuBar
(при этом меню располагаются слева направо), либо посредством метода s e t H e l p -
Menu (в этом случае меню помещается в крайнюю правую позицию строки). Готовый
объект MenuBar включается в окно Frame с помощью метода s e t M e n u B a r этого объ­
екта. Для поддержки событий с каждым из меню необходимо связать обработчик
A c t i o n L i s t e n e r . Для определения пункта меню, выбранного пользователем, можно
обратиться к методу e v e n t . g e t A c t i o n C o m m a n d . В листинге 13.12 приведен пример,
в котором создается меню для выбора цвета и в соответствии с решением пользовате­
ля устанавливается цвет фона для окна Frame. Результаты выполнения программы
показаны на рис. 13.8.

Листинг 1 3 . 1 2 . C o l o r M e n u . J a v a

import java.awt.*;
import Java.awt.event.*;
/ * * Демонстрирует с о з д а н и е меню окна Frame

p u b l i c c l a s s ColorMenu e x t e n d s C l o s e a b l e F r a m e
implements A c t i o n L i s t e n e r {

p r i v a t e S t r i n g [ ] colorNames =
{ " B l a c k " , " W h i t e " , " L i g h t G r a y " , "Medium G r a y " ,
"Dark G r a y " };
private Color[] colorValues =
{ Color.black, Color.white, Color.lightGray,
C o l o r . g r a y , C o l o r . d a r k G r a y };

p u b l i c ColorMenu0 {
super("ColorMenu");
MenuBar b a r = new M e n u B a r ( ) ;
Menu ColorMenu = new M e n u ( " C o l o r s " ) ;
f o r ( i n t i = 0 ; i < 2 ; i++) {
ColorMenu.add(colorNames[i]);
}
Menu grayMenu = new M e n u ( " G r a y " ) ;
f o r ( i n t i = 2 ; i < c o l o r N a m e s . l e n g t h ; i++) {
grayMenu.add(colorNames[i]) ;
}
ColorMenu.add(grayMenu);
bar.add(ColorMenu) ;
setMenuBar(bar) ;
ColorMenu.addActionListener(this);
grayMenu.addActionListener(this);
1 3 . 8 . Класс Frame 481

setBackground(Color.lightGray);
setSize(400, 200);
setVisible(true) ;
}

/** Обработка событий, связанных с меню в объекте Frame. */

public void actionPerformed(ActionEvent event) {


setBackground(colorNamed(event.getActionCoramand()));
repaint();
}

private Color colorNamed(String colorName) {


for(int i=0; i<colorNames.length; i++) {
if(colorNames[i].equals(colorName) ) {
return(colorValues[i]);
}
}
return(Color.white);
}
public static void main(String[] args) {
new ColorMenu();

Рис. 13.8. Окно Frame, содержащее


строку меню

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


Ниже приведено краткое описание методов класса Frame. Данный класс наследует
также все методы классов Component и C o n t a i n e r .

public void disposeO


Данный метод удаляет платформенно-зависимое окно, соответствующее Frame, и все
вложенные компоненты. Поскольку из аплета нельзя вызвать метод System, e x i t , ок­
но Frame, созданное в аплете, должно передавать событие WINDOW_DESTROY.
М е т о д и к а профессионалов

Для того чтобы закрыть окно Frame, созданное в аплете, надо вме­
сто System, exit использовать метод dispose.
482 Глава 13. Компоненты AWT

public Image getIconImage()


public void s e t I c o n I m a g e ( I m a g e icon)
Данные методы позволяют получить или изменить изображение, которое исполь­
зуется для представления окна Frame в виде пиктограммы. Если раньше не был
вызван метод s e t l c o n l m a g e , метод g e t I c o n Image вернет значение n u l l .

public MenuBar getMenuBar()


public void setMenuBar(MenuBar menuBar)
p u b l i c void r e m o v e ( M e n u C o m p o n e n t menuBar)
Данные методы позволяют получить или изменить строку меню. Процесс созда­
ния строки меню был описан выше в этом разделе.

public String getTitleO


public void setTitle(String title)
Указанные методы дают возможность получить или заменить текст, отображае­
мый в заголовке окна.

public b o o l e a n isResizable()
public void s e t R e s i z a b l e ( b o o l e a n resizeFlag)
По умолчанию пользователям разрешается изменять размеры созданного вами
окна. Вызвав s e t R e s i z a b l e ( f a l s e ) , вы запретите изменение размеров. Метод
i s Re s i z a b l e позволяет определить текущую установку.

public void раск()


Метод р а е к изменяет размеры окна Frame в соответствии с размерами содержа­
щихся в нем компонентов.

public void toBack()


public void toFront()
Данные методы помещают Frame на задний или передний план (т.е. за окнами,
перекрывающими Frame, или поверх их).

13.9. Сериализация окон


В Java 1.1 появилась возможность сериалггзации объектов. Данное средство позво­
ляет сохранять информацию о текущем состоянии Java-объектов на диске или переда­
вать его по сети. С помощью одной команды ( w r i t e O b j e c t ) вы можете сохранить со­
стояние Frame или другого окна и всех подчиненных окон, включая информацию о
размере, цвете и управляющих элементах. На выполнение данной процедуры накла­
дывается лишь одно ограничение: чтобы состояние окна было сохранено, все объек­
ты, на которые ссылается это окно, должны допускать сериализацию. Этому требова­
нию удовлетворяют все элементы AWT. Для обеспечения поддержки объектом сериа-
лизации надо, чтобы этот объект реализовывал интерфейс S e r i a l i z a b l e . В данном
13.9. Сериализация окон 483

интерфейсе не объявлены никакие методы, поэтому вам достаточно включить в оп­


ределение класса выражение implements S e r i a l i z a b l e . Если значение какого-
либо из полей объекта не обязательно сохранять на диске, вам надо объявить это по­
ле как t r a n s i e n t . Состояние окна восстанавливается также с помощью одной коман­
ды (readObject).

Запись окна на диск


Следующий фрагмент кода демонстрирует процесс сохранения состояния окна на
диске. При этом предполагается, что в данной программе импортирован пакет
Java.io.*.
try {
FileOutputStream fileOut =
new FileOutputStream("SaveFilename");
ObjectOutputStream out =
new ObjectOutputStream(fileOut);
out.writeObject(someWindow);
out.flush ();
out.close ();
} catch(lOException ioe) {
S y s t e m . o u t . p r i n t l n ( " E r r o r saving window: " + i o e ) ;
}

Если вы хотите сэкономить место на диске, вам надо включить GZIPOutput-


Stream (он содержится в пакете j a v a . u t i l . z i p ) между F i l e O u t p u t S t r e a m и
ObjectOutputStream. Однако сериализованные окна достаточно компактны и без
сжатия.

Чтение окна с диска


Прочитать текущее состояние окна с диска позволяет следующий простой фраг­
мент кода.
try {
File saveFile = new File("SaveFilename");
FilelnputStream fileln =
new FilelnputStream(saveFile);
ObjectlnputStream in =
new ObjectInputStream(fileln);
someWindow = (WindowType)in.readObject();
doSomethingWith(someWindow); // Например, setVisible.
} catch(lOException ioe) {
System.out.println("Error reading file: " + ioe);
} catch(ClassNotFoundException cnfe) {
System.out.println("No such class: " + cnfe);
}
Если при сохранении на диске информация о состоянии окна была сжата, надо
включить GZIPInputStream между FilelnputStream и ObjectlnputStream.
484 Глава 13. Компоненты AWT

Пример окна Frame, допускающего сохранение


на диске
в листинге 13.13 показано создание окна Frame, которое обеспечивает рисование
кругов после щелчка м ы ш ь ю в поле окна. Нарисовав несколько кругов, переместив
окно на экране и изменив его размеры, вы можете активизировать кнопку Save для
сохранения результатов на диске. На рис. 13.9 показано состояние окна после того,
как пользователь нарисовал в нем несколько кругов, завершил работу программы, а
затем перезапустил ее в следующем сеансе. Класс SavedFrame использует два вспомо­
гательных класса. Класс Circle Panel (его код показан в листинге 13.14) представля­
ет собой объект Panel с обработчиком MouseListener, который добавляет круги в
тех позициях экрана, где пользователь щелкнул мышью. Сами круги представляют со­
бой экземпляры класса BetterCircle, показанного в листинге 13.4.

Листинг 13.13.SavedFrame.Java

import java.awt.*;
import java.awt.event.*;
import java.io.*;

/** Окно Frame отображает круги после щелчков мышью.


* Текущее состояние окна сохраняется на диске.
V

public class SavedFrame extends CloseableFrame


implements ActionListener {

/** Если существует сохраненная версия окна, она


* отображается на экране. В противном случае
* создается новое окно.
*/
public static void main(String[] args) {
SavedFrame frame;
File serializeFile » new File(serializeFilename);
if (serializeFile.exists 0 ) {
try {
FilelnputStreeun fileln =
new FilelnputStream(serializeFile);
ObjectlnputStream in = new ObjectlnputStream(fileln);
frame = (SavedFrsune) in. readObject () ;
frame.setVisible(true);
} catch(lOException ioe) {
System.out.println("Error reading file: " + ioe);
} catch(ClassNotFoundException cnfe) (
System.out.println("No such class: " + cnfe);
}
} else {
frame = new SavedFrame();
}
13.9. Сериализация окон 485

private static String serializeFilename ="SavedFrame.ser";


private CirclePanel circlePanel;
private Button clearButton, saveButton;

/** Создание окна, содержащего CirclePanel и кнопки. */

public SavedFrameO {
super("SavedFrame");
setBackground(Color.white) ;
setFont(new Font("Serif", Font.BOLD, 18));
circlePanel = new CirclePanel();
add("Center", circlePanel);
Panel buttonPanel = new Panel();
buttonPanel.setBackground(Color.lightGray);
clearButton = new Button("Clear");
saveButton = new Button("Save");
buttonPanel.add(clearButton);
buttonPanel.add(saveButton);
add(buttonPanel, BorderLayout.SOUTH);
clearButton.addActionListener(this);
saveButton.addActionListener(this);
setSizeOOO, 300) ;
setVisible(true);
}
/** После щелчка на кнопке Clear удаляются все нарисованные
* Кнопка Save позволяет сохранить на диске текущее
* состояние окна (размеры, расположение, содержимое и др.)
V
public void actionPerformed(ActionEvent event) {
if (event.getSource() == clearButton) {
circlePanel.removeAll();
circlePanel.repaint();
} else if (event.getSource() == saveButton) {
try {
FileOutputStream fileOut =
new FileOutputStreamC'SavedFraiae. ser") ;
ObjectOutputStream out =
new ObjectOutputStream(fileOut);
out.writeObject(this);
out.flush();
out.close 0 ;
} catch(lOException ioe) {
System.out.println("Error saving frame: " + ioe);
}
}
486 Глава 13. Компоненты AWT

Листинг 13.14. CirclePanel. Java

import java.awt.*;
import Java.awt.event.*;
import java.io.*;
/** Панель, в которой отображаются круги с центром в точке,
* на которую указывает курсор мыши.
* <В> Диспетчер компоновки не используется.</В>.

public class CirclePanel extends Panel {


class ClickAdapter extends MouseAdapter
implements Serializable {
public void mouseClicked(MouseEvent event) {
BetterCircle circle = new BetterCircle(Color.black, 25)
add(circle);
circle.setCenter(event.getX(), event.getY());
invalidate();
validate();
}
}
public CirclePanel()
:) {
setLayout(null);
addMouseListener(new ClickAdapter());

Рис. 13.9. Сериализация позволяет


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

13.10. Класс Dialog


Класс D i a l o g представляет собой упрощенный вариант класса Frame, используе­
мый в приложениях, для которых не требуются все возможности Frame. Класс
D i a l o g обычно выступает в одной из следующих ролей.
1. Модальное диалоговое окно, которое запрещает взаимодействие с другими ком­
понентами AWT до тех пор, пока это окно не будет закрыто. Подобный режим
применяется в тех случаях, когда пользователь должен выполнить определенные
действия перед тем, как выполнение программы может быть продолжено.
13.10. Класс Dialog 487

2. Упрощенное окно Frame (в котором отсутствуют курсор, меню и изображение


пиктограммы). Немодальный диалог организуется так же, как и в классе Frame,
но при этом требуется меньше ресурсов и увеличивается быстродействие.
С объектом D i a l o g связывается диспетчер компоновки B o r d e r L a y o u t , который
делит окно на пять областей: NORTH, SOUTH, EAST, WEST и CENTER. Подробно о дис­
петчере компоновки B o r d e r L a y o u t см. в главе 12.

Создание и использование окна Dialog


Окно D i a l o g используется аналогично окну Frame, за исключением того, что кон­
структору передаются два дополнительных параметра: родительское окно Frame и
значение b o o l e a n , определяющее, должно ли диалоговое окно быть модальным.
Dialog d i a l o g =
new D i a l o g ( p a r e n t F r a m e , titleString, false);
Dialog modalDialog =
new D i a l o g ( p a r e n t F r a m e , titleString, true);
Для того чтобы использовать диалоговое окно в аплете, надо получить родитель­
ское окно посредством метода g e t Frame, который был описан выше в этой главе.
Модальное диалоговое окно запрещает все действия с компонентами, выполняемые с
помощью мыши и клавиатуры, и откладывает вызовы других потоков.

Пример диалогового окна: подтверждение


завершения программы
в листинге 13.5 показан фрагмент кода, с помощью которого создается диалоговое
окно, содержащее две кнопки. Посредством данного окна пользователь подтверждает
свои действия по завершению программы. Щелчок на кнопке Yes означает выход из
программы. После щелчка на кнопке No диалоговое окно закрывается, но программа
продолжает выполнение. В листинге 13.16 диалоговое окно связывается с событием
WINDOWDESTROY так, что оно отображается при попытке пользователя закрыть ос­
новное окно программы. Результаты выполнения показаны на рис. 13.10.

Листинг 1 3 . 1 5 . C o n f i r m . J a v a

import java.awt.*;
import Java.awt.event.*;

/ • • Модальное д и а л о г о в о е окно с кнопками Yes и No.


* После щелчка на кнопке Yes выполнение программы
* з а в е р ш а е т с я . Щелчок на кнопке No приводит лишь
* к закрытию д и а л о г о в о г о о к н а .
Ч
c l a s s Confirm e x t e n d s Dialog implements A c t i o n L i s t e n e r {
p r i v a t e Button yes, no;

p u b l i c Confirm(Frame p a r e n t ) {
488 Глава 13. Компоненты AWT

super(parent, "Confirmation", true);


setLayout(new FlowLayout());
add(new Label("Really quit?"));
yes = new Button("Yes");
yes.addActionListener(this);
no = new Button("No");
no.addActionListener(this);
add(yes);
add(no);
packO ;
setVisible(true);
}
public void actionPerformed(ActionEvent event) {
if (event.getSource0 == yes) {
System.exit(0);
} else {
dispose 0 ;
}
}

Листинг 13.16.ConfirmTest.Java

import java.awt.*;
import j ava.awt.event.*;

/** Окно Frame, использующее диалоговое окно Confirm


* для подтверждения намерений пользователя завершить
* выполнение программы.
*/
public class ConfirmTest extends Frame {
public static void main(String[] args) {
new ConfirmTest();
}
public ConfirmTest0 {
super("Confirming QUIT");
setSize(200, 200);
addWindowListener(new ConfirmListener());
setVisible(true);
}
public ConfirmTest(String title) {
super(title);
}
private class ConfirmListener extends WindowAdapter {
public void windowClosing(WindowEvent event) {
new Confirm(ConfirmTest.this);
}
}
1 3 . 1 1 . Класс FileDialog 489

)^;г|ДДЛ^Р^ЗЯ^

»6^yeiy)i^
||ц^||^^|Ш Рис. 13.10. Модальное диалоговое окно
запрещает все действия с другими
компонентами

13.11. Класс FileDialog


F i l e D i a l o g — это специальный тип модального диалогового окна, используемого
для загрузки и сохранения файлов. Объекты F i l e D i a l o g используются только в при­
ложениях, так как аплеты не имеют доступа к локальному диску. Поскольку класс
F i l e D i a l o g представляет собой контейнер, он может содержать другие компонен­
ты. С объектом данного типа не связывается диспетчер компоновки. Ниже приведе­
ны основные действия по созданию объекта F i l e D i a l o g .
// Создание объекта FileDialog.
FileDialog fileLoader =
new FileDialog(frame, title, FileDialog.LOAD);
FileDialog fileSaver =
new FileDialog(frame, title, FileDialog.SAVE);
// Установка файла или типа файлов по умолчанию.
fileLoader.setFile("*.txt");
// Отображение FileDialog.
fileLoader.show();
// Определение выбранного имени файла.
String filename = fileLoader.getFile();

Пример отображения файлов с помощью


компонента TextArea
в листинге 13.17 показан код, который создает окно Frame. Основную часть окна
занимает текстовая область (компонент TextArea), но в нижней его части отобража­
ется кнопка (рис. 13.11). После активизации данной кнопки выводится диалоговое
окно F i l e D i a l o g , также показанное на рис. 13.11, которое позволяет пользователю
выбрать файл. После выбора файла создается объект F i l e l n p u t S t r e a m и содержи­
мое файла отображается в текстовой области (рис. 13.12).

Листинг 1 3 . 1 7 . D i s p l a y F i l e . J a v a

import j a v a . a w t . * ;
import J a v a . a w t . e v e n t . * ;
import j a v a . i o . * ;
/** Использование F i l e D i a l o g для выбора файла. */
p u b l i c c l a s s D i s p l a y F i l e extends CloseableFrame
490 Глава 13. Компоненты AWT

implements ActionListener {

public static void main(String[] args) {


new DisplayFile0;
}
private Button loadButton;
private TextArea fileArea;
private FileDialog loaders-
public DisplayFile() {
super("Using FileDialog");
loadButton = new Button("Display File");
loadButton.addActionListener(this);
Panel buttonPanel = new Panel();
buttonPanel.add(loadButton);
add(buttonPanel, BorderLayout.SOUTH);
fileArea = new TextArea();
add("Center", fileArea);
loader ~ new FileDialog(this, "Browse", FileDialog.LOAD)
// Расширение файла по умолчанию: .Java.
loader.setFile("*.Java");
setSize(350, 450);
setVisible(true);

/** После щелчка на кнопке открывается диалоговое окно


* выбора файла. После закрытия диалогового окна
* начинается загрузка файла.
V
public void actionPerformed(ActionEvent event) {
loader.show();
di splay File (loader .getFileO ) ;
}
public void displayFile(String filename) {
try {
File file = new File(filename);
FilelnputStream in = new FilelnputStream(file);
int fileLength = (int)file.length();
byte[] fileContents = new byte[fileLength];
in.read(fileContents);
String fileContentsString = new String(fileContents)
fileArea.setText(fileContentsString);
} catch(lOException ioe) {
fileArea.setText("lOError: " + ioe);
}
}
13.12. Класс Window 491

import Java awt*,


I m p о rt J ava. awt. eve nt.*.
import Java.io.*;
1?||№^Я|
[Г* Uses a FileDialog to choose the file to display */
LoQkiK ^ Awt

J!*] BetterScrolbai lava ^ Choice? e$J lava aJColorMenujava public class DisplayFile extends CloseableFrame
«] ColofPopupMenu implements ActionListener{
j j l BgReporter lava a j C»cte lava
e] BuHonExampte lava ^ C»clePanel.|ava Mj Confirm. Java
public static void main(StringQ args) {
jej Button! e$(1|ava ;^C«cleTe$t|ava jgjContirmTest.iava
nev^^DisplayFileO;
.<i| ButtonTesl2.iava i ^ CircleTestZiava
Щ Checkboxes. Java ^ CioseabieFrdme.iava iMj FgRepoitet java
private Button loadButton;
b] I private TextArea fileArea;
private FileDialog loader;
Fjtejoome:

FiwoTj**.
JDisplayFile.iava

JAHFilesf"") 3
Qp«n
3 public DisplayFileO{
superfUsing FileDialog^;
loadButton = new ButtonCDisplay File"),
loadButton.addActionListener(this);
Panel buttonPanel = nev^ PaneiQ,
buttonPanel add(loadButton),
add(buttonPanel, BorderLayout SOUTH).

Ы ^ 1^:
gj^e^^f^l [pteggFjgi

Рис. 13.11. В окне Frame отображаются кнопка и Рис. 13.12. После того как пользо­
объект TextArea. При активизации кнопки вызывает­ ватель выберет файл, его содержи­
ся окно F i l e D i a l o g мое отобразится в области TextArea

13.12. Класс Window


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

Диспетчер компоновки по умолчанию:


BorderLayout
Данный диспетчер компоновки делит окно на пять областей: NORTH, SOUTH, EAST,
WEST и CENTER. Подробно о диспетчере B o r d e r L a y o u t см. в главе 12.

Создание и использование объекта Window


П р и создании объекта Window вы можете либо явно задать размеры окна, либо
указать, что размеры должны быть определены исходя из размеров и размещения
компонентов, содержащихся в этом окне.
Для создания окна Window фиксированных размеров используется фрагмент кода,
подобный следующему:
// Создание объекта Window.
Window window = new Window(parentFrame);
// Добавление компонентов к окну.
window.add(somePanel, BorderLayout.CENTER);

// Определение размеров окна,


492 Глава 13. Компоненты AWT

window.setSize(width, height);
// либо указание размеров и позиции,
size.setBounds(left, top, width, height);
// Отображение объекта Window,
window.setVisible(true);
Окно Window, размеры которого вычисляются на основе размеров содержащихся
в нем компонентов, создается следующим образом.
// Создание объекта Window.
Window window = new Window(parentFrame);
// Позиционирование окна (необязательное действие).
window.setLocation(left, top);
// Добавление компонентов.
winddow.add(somePanel, BorderLayout.CENTER);

// Изменение размеров окна,


window.pack();
// Отображение объекта Frame,
window.setVisible(true);
Поскольку с Window связан диспетчер компоновки BorderLayout, при добавле­
нии компонента в окно надо указывать область, в которую этот компонент должен
быть помещен. Если вы вызовете метод window, add (someComponent), то компо­
нент по умолчанию будет включен в область CENTER. Последующие вызовы
add (someOtherComponent) заменят компонент в области CENTER новым.

13.13. Обработка событий управляющих


элементов
В данном разделе мы рассмотрим общий подход к обработке событий, связанных с
элементами графического пользовательского интерфейса. В зависимости от компо­
нента генерируются различные события. Так, например, объект Button генерирует
событие ActionEvent, а Checkbox — ItemEvent. Как было сказано в главе 10, суще­
ствуют низкоуровневые и высокоуровневые средства обработки событий. Эти два
уровня обработки кратко описаны ниже.
• Высокоуровневая обработка событий. С компонентом связывается обработ­
чик addXJcxListener, где Хх:х соответствует типу события, которое генерирует
компонент. При возникновении события вызывается метод, определенный в
составе обработчика.
• Низкоуровневая обработка событий. В первую очередь необходимо разре­
шить низкоуровневую обработку событий; это осуществляется с помощью ме­
тода e n a b l e E v e n t s с соответствующей маской AWTEvent. Обработка события
осуществляется с помощью метода ргосеssXxxEvent, где Ххх задает тип со­
бытий, которые могут генерироваться компонентом.
С различными компонентами связаны разные типы событий, каждый из которых
описывается подклассом класса AWTEvent. В частности, событие ActionEvent гене­
рируется объектами Button, L i s t , Menultem и T e x t F i e l d и обрабатывается с по-
13.13. Обработка событий управляющих элементов 493

мощью A c t i o n L i s t e n e r либо p r o c e s s A c t i o n E v e n t . Событие, имеющее отноше­


ние к процессу выбора, генерируется объектами Checkbox, CheckboxMenuItem,
Choice и L i s t и обрабатывается посредством I t e m L i s t e n e r либо p r o c e s s l t e m -
Event. Поля редактирования и текстовые области генерируют событля клавиатуры
(KeyEvent) и текста (TextEvent); они обрабатываются посредством K e y L i s t e n e r и
T e x t L i s t e n e r либо processKeyEvent и p r o c e s s T e x t E v e n t . Полосам прокрутки
соответствует событие AdjustmentEvent, для обработки которого используется
A d j u s t m e n t L i s t e n e r либо p r o c e s s A d j u s t m e n t E v e n t . С мышью и клавиатурой
связаны события MouseEvent и KeyEvent, которые передаются всем компонентам за
исключением Menultem и CheckboxMenuItem.
Для поддержки высокоуровневых событий используются два подхода. Вы можете
предоставить компоненту самостоятельно обрабатывать свои события либо обраба­
тывать события централизованно с помощью внешнего объекта. Здесь мы опишем
эти два подхода применительно к событиям ActionEvent. Обработка других собы­
тий осуществляется аналогично.
На рис. 13.13 показано окно Frame с тремя кнопками. При активизации кнопки
пользователем изменяются размеры окна. Если не принимать во внимание заголовок
окна, децентрализованная и централизованная обработка событий дают одинаковые
результаты.

Рис, 13.13. Внешний вид окна: а— после активизации первой кнопки;


б — после активизации второй кнопки

Децентрализованная обработка событий


При децентрализованной обработке событий каждый компонент полностью от­
вечает за обработку собственных событий, для чего в классе реализуются соответст­
вующие методы. Пользователи такого компонента не обязаны знать, как именно про­
исходит обработка. Данный способ поддержки событий соответствует объектно-
ориентированному подходу к разработке программ. Однако информация о контейне­
ре, содержащем компонент, непосредственно недоступна и компонент должен быть
относительно независим от приложения, в котором он используется. В качестве при­
мера децентрализованной обработки событий предположим, что вам нужен элемент
графического пользовательского интерфейса, обрабатывающий события A c t i o n -
Event. В первую очередь вам надо создать объект, реализующий интерфейс A c t i o n -
L i s t e n e r , а затем определить метод с именем a c t i o n P e r f ormed, которому при вы-
494 Глава 13. Компоненты AWT

зове передается объект A c t i o n E v e n t . Для связывания метода, предназначенного для


обработки события, с самим объектом используется a d d A c t i o n L i s t e n e r . В данном
случае объект обрабатывает только события A c t i o n E v e n t .
В листингах 13.18 и 13.19 показан обработчик события, созданный с помощью де­
централизованного подхода. Подкласс класса B u t t o n реализует интерфейс
A c t i o n L i s t e n e r и содержит метод a c t i o n P e r f o r m e d , предназначенный для обра­
ботки событий A c t i o n E v e n t , сгенерированных самим объектом. Вызовом метода
a d d A c t i o n L i s t e n e r ( t h i s ) в конструкторе класса объект определяется как обра­
ботчик собственных событий. П р и активизации пользователем кнопки ширина и вы­
сота контейнера изменяются. Посредством методов i n v a l i d a t e и v a l i d a t e вклю­
чающий контейнер получает указание повторно разместить компоненты и отобра­
зить их на экране. Создавая исходный текст программы, в него обязательно надо
включить выражение i m p o r t j a v a . a w t . e v e n t . * ; , так как в этом пакете определе­
ны все типы событий.

Листинг 1 3 . 1 8 . A c t i o n E x a m p l e l , Java

import java.awt.*;
public class ActionExamplel extends CloseableFrame {
public static void main(String[] args) {
new ActionExamplel();
}
public ActionExamplel() {
super("Handling Events in Component");
setLayout(new FlowLayout());
setFont(new Font("Serif", Font.BOLD, 18));
add(new SetSizeButton(300, 200)),
add(new SetSizeButton(400, 300))
add(new SetSizeButton(500, 400))
setSize(400, 300);
setVisible(true);
}

Листинг 13.19.SetSizeButton.Java

import Java.awt.*;
import j ava.awt.event,*;

public class SetSizeButton extends Button


implements ActionListener
private int width, height;
private Container parent;

public SetSizeButton(int width, int height) {


super("Resize to " + width + "x" + height);
this.width = width;
this.height = height;
13.13. Обработка событий управляющих элементов 495

parent = getParentO;
addActionListener(this)

p\iblic void actionPerformed (ActionEvent event) {


parent.setSize(width, height);
parent.invalidate();
parent.validate();
}

Централизованная обработка событий


Второй способ — централизованная обработка событий, при которой события,
сгенерированные различными компонентами, передаются одному объекту A c t i o n -
L i s t e n e r . Недостатком такого подхода является тот факт, что перед тем как присту­
пить непосредственно к обработке, обработчик должен предпринять меры по опре­
делению источника, сгенерировавшего событие. Однако в этом случае из метода
a c t i o n P e r f ormed вы можете непосредственно обращаться к полям и методам объ­
екта A c t i o n L i s t e n e r , в роли которого часто выступает контейнер, содержащий
компоненты.
В листинге 13.20 показан пример централизованной обработки событий. В данном
случае объектом прослушивания для всех трех кнопок является контейнер. Незави­
симо от того, какая из кнопок стала источником события ActionEvent, обработка
осуществляется в теле метода а с t i o n P e r f o r m e d , принадлежащего контейнеру. Не­
достатком такого подхода является необходимость определения кнопки, сгенериро­
вавшей событие, но при этом упрощается доступ к методу updateLayout, используе­
мому для изменения размеров окна.
Разновидностью описанного подхода является создание отдельного внутреннего
класса (реализующего интерфейс A c t i o n L i s t e n e r ) для каждой кнопки. Преимуще­
ство такого способа состоит в том, что внутренние классы имеют непосредственный
доступ к полям и методам окна, а отдельные объекты A c t i o n L i s t e n e r , связанные с
каждой кнопкой, избавляют разработчика от необходимости включать в программу
дополнительный код по выявлению источника события. Об использовании внутрен­
них классов ддя обработки событий см. в разделе 11.3.

Листинг 13.20. ActionExample2 , java

import j a v a . a w t . * ;
import j a v a . a w t . e v e n t . * ;
p u b l i c c l a s s ActionExample2 extends CloseableFrame
implements ActionListener {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
new ActionExample2();
}
p r i v a t e Button b u t t o n l , b u t t o n 2 , b u t t o n S ;
p u b l i c ActionExample2() {
super("Handling Events in Other O b j e c t " ) ;
496 Глава 13. Компоненты AWT

setLayout(new FlowLayout());
setFont(new Font("Serif", Font.BOLD, 18))
buttonl = new Button("Resize to 300x200")
button2 = new Button("Resize to 400x300")
button3 = new Button("Resize to 500x400")
buttonl.addActionListener(this) ;
button2.addActionListener(this);
button3.addActionListener(this);
add(buttonl);
add(button2);
add(button3);
setSize(400, 300);
setvisible(true);
}
public void actionPerformed(ActionEvent event) {
if (event.getSource() == buttonl) {
updateLayout(300, 200);
} else if (event.getSource0 == button2) {
updateLayout(400, 300);
} else if (event.getSource0 == button3) {
updateLayout(500, 400);
}
}
private void updateLayout(int width, int height) {
setSize(width, height);
invalidate();
validate();
}

13.14. Класс Button


Класс Button создает элемент интерфейса, который представляет собой кнопку с
надписью. В языке программирования Java отсутствуют встроенные классы, предна­
значенные для обработки кнопок с изображениями. Чаще всего в программе создает­
ся кнопка с надписью (меткой), помещается в окно, а затем анализируются события,
генерируемые при активизации кнопки пользователем. Для того чтобы создать кноп­
ку и включить ее в окно, надо выполнить следующие действия:
Button button = new Button("...");
add(button);

Конструкторы
в классе Button предусмотрены следующие два конструктора.

public ButtonO
public Button(String buttonLabel)
Первый конструктор создает кнопку без надписи. Надпись (метку) можно доба­
вить позже, воспользовавшись для этого методом s e t L a b e l . Даже если надпись
13.14. Класс Button 497

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


p r e f e r r e d S i z e , больше нуля, поскольку границы элемента занимают определен­
ное место. Второй конструктор создает кнопку с указанной надписью. В этом слу­
чае ширина и высота кнопки, возвращаемые методом p r e f e r r e d S i z e (данные
сведения используются диспетчером компоновки), вычисляются на основе разме­
ров надписи, отображаемой текущим шрифтом; при этом учитываются размеры
границ, которые могут быть различными в разных операционных системах.

Пример аплета с тремя кнопками


в листинге 13.21 приведен код аплета, содержащего три кнопки. Поскольку мы не
выполняем с кнопками никаких действий, нам не нужны ссылки на экземпляры объ­
ектов Button. Для включения кнопок в контейнер используются выражения add (new
Button ( . . . ) ) . Если же вы планируете впоследствии добавить операции с кнопками,
можете сохранить ссылки на них в переменных. На рис. 13.14 показаны результаты
выполнения кода, представленного в листинге 13.21, в броузере Netscape 4.7, рабо­
тающем в операционной системе Windows 98.

Листинг 13.21. Buttons. Java

import j a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
p u b l i c c l a s s Buttons extends Applet {
p r i v a t e Button b u t t o n l , b u t t o n 2 , b u t t o n s ;
p u b l i c void i n i t O {
b u t t o n l = new Button("Button One");
button2 = new Button("Button Two");
b u t t o n s = new Button("Button T h r e e " ) ;
add(buttonl);
add(button2) ;
add(buttons) ;
}

шшшшшшшшвшшяяиошш^
' ^ ^ a 5} ^, за -1. iT :!i и di
Buttons
pOn« BietonTwo Button Thf«»j

Рис. 13.14. Кнопки, отображаемые


Java-аплетом в системе Windows 98
498 Глава 13. Компоненты AWT

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


Ниже описаны наиболее часто используемые методы класса B u t t o n .

public String getLabel()


Метод g e t L a b e l позволяет определить надпись на кнопке, отображаемую в дан­
ный момент.

public v o i d setLabel(String newLabel)


Метод s e t L a b e l изменяет надпись на кнопке. Если кнопка уже отображается на
экране, замена надписи не приведет к изменению размеров элемента. Чтобы на
экране было выведено новое изображение кнопки, надо вызвать методы
i n v a l i d a t e и v a l i d a t e включающего окна. П р и этом размеры окна могут также
измениться в соответствии с новыми размерами кнопки.
s o m e B u t t o n . s e t L a b e l ( " А New L a b e l " ) ;
someButton.getParent().invalidate();
someButton.getParent().validate();

public v o i d addActionListener(ActionListener listener)


public void r e m o v e A c t i o n L i s t e n e r ( A c t i o n L i s t e n e r listener)
Метод a d d A c t i o n L i s t e n e r добавляет, a метод r e m o v e A c t i o n L i s t e n e r удаляет
объект прослушивания, предназначенный для обработки событий A c t i o n E v e n t .
Использование этих методов будет рассмотрено ниже в этой главе.

public void processActionEvent(ActionEvent event)


Этот метод предназначен для низкоуровневой обработки событий и используется
в тех случаях, когда кнопка должна сама обрабатывать свои события. П р и исполь­
зовании данного метода необходимо разрешить передачу событий с помощью ме­
тода e n a b l e E v e n t s . Кроме того, переопределяя p r o c e s s A c t i o n E v e n t , не за­
будьте включить в тело данного метода вызов s u p e r . p r o c e s s A c t i o n E v e n t . Кон­
кретные примеры будут приведены в следующем разделе.

Кроме указанных методов, класс B u t t o n , как и любой компонент, содержит


getForeground, setForeground, getBackground, setBackground, getFont,
s e t F o n t и другие методы класса Component.

Поддержка действий с кнопками


Способ, посредством которого среда Java определяет, что кнопка была активизи­
рована, зависит от конкретной операционной системы. Обычно активизация проис­
ходит после щелчка мышью на кнопке либо нажатия клавиши <Enter> в тот момент,
когда фокус ввода принадлежит данной кнопке.
Подобно другим событиям, события, связанные с кнопками, могут обрабатываться
двумя способами. Если объект B u t t o n должен сам выполнять необходимую обработ­
ку, надо разрешить передачу событий A c t i o n E v e n t с помощью следующего вызова:
enableEvents(AWTEvent.ACTION EVENT MASK);
13.14. Класс Button 499

Далее следует переопределить метод p r o c e s s A c t i o n E v e n t , реализовав в нем не­


обходимые действия по обработке. В состав переопределенного метода надо вклю­
чить вызов super . p r o c e s s A c t i o n E v e n t , чтобы могли быть вызваны объекты про­
слушивания, связанные с данной кнопкой. Ниже приведен фрагмент кода, реализую­
щий указанные действия. Реальный пример использования p r o c e s s A c t i o n E v e n t
представлен в листинге 13.19.
p u b l i c void processActionEvent(ActionEvent event) {
takeSomeAction(...);
s u p e r . p r o c e s s A c t i o n E v e n t ( e v e n t ) ; / / Поддержка обработчиков.
}
Помимо обработки событий непосредственно объектом Button, можно связать с
кнопкой один или несколько объектов A c t i o n L i s t e n e r . Следуя данному подходу, надо
создать объект, реализующий интерфейс A c t i o n L i s t e n e r , и связать его с кнопкой с
помощью метода a d d A c t i o n L i s t e n e r . В тело метода actionPerformed, который вы­
зывается при активизации кнопки, включается код, обрабатывающий событие.
В листинге 13.20 класс окна реализовывал интерфейс A c t i o n L i s t e n e r , но это не
единственный способ создания объекта прослушивания. Например, в листинге 13.22
создаются три кнопки, взаимодействующие с тремя объектами A c t i o n L i s t e n e r . С
первой из кнопок связывается обработчик FgReporter (листинг 13.23), со второй —
FgReporter и BgReporter (листинг 13.24), а с третьей— FgReporter, BgReporter
и S i z e R e p o r t e r (листинг 13.25). Каждый из этих объектов реализует интерфейс
A c t i o n L i s t e n e r и выводит некоторые данные о компоненте, который выступил в
роли источника события. Результаты выполнения кода приведены на рис. 13.15, а
данные, выведенные после активизации всех трех кнопок, показаны в листинге 13.26.

Листинг 13.22. ButtonExample. Java

import j a v a . a w t . * ;
import J a v a . a w t . e v e n t . * ;
p u b l i c c l a s s ButtonExample extends CloseableFrame {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
new ButtonExample();
}

p u b l i c ButtonExample() {
super("Using A c t i o n L i s t e n e r s " ) ;
setLayout(new FlowLayout());
Button bl = new Button("Button 1")
Button b2 = new Button("Button 2")
Button b3 = new Button("Button 3")
bl.setBackground(Color.lightGray);
b2.setBackground(Color.gray);
b3.setBackground(Color.darkGray) ;
FgReporter fgReporter = new F g R e p o r t e r ( ) ;
BgReporter bgReporter = new BgReporter();
SizeReporter s i z e R e p o r t e r = new S i z e R e p o r t e r ( ) ;
bl.addActionListener(fgReporter);
b2.addActionListener(fgReporter);
b2.addActionListener(bgReporter);
500 Глава 13. Компоненты AWT

ЬЗ.addActionListener(fgReporter);
ЬЗ.addActionListener(bgReporter);
ЬЗ.addActionListener(sizeReporter)
add(bl)
add(b2)
add(b3)
setSize(350, 100) ;
setVisible(true);

Листинг 13.23. FgReporter. Java

import Java.awt.event.*;
import java.awt.*;

public class FgReporter implements ActionListener {


public void actionPerformed(ActionEvent event) {
Component с = (Component)event.getSource();
System, out. println( "Foreground: " -f c.getForeground () ) ;
}
}

Листинг 13.24. BgReporter. Java

import java.awt.event.*;
import java.awt.*;

public class BgReporter implements ActionListener {


public void actionPerformed(ActionEvent event) {
Component с = (Component)event.getSource();
System.out.println("Background: " + c.getBackground());
}
}

Листинг 13.25.SizeReporter.Java

import Java.awt.event.*;
impo rt j ava.awt.*;

public class SizeReporter implements ActionListener {


public void actionPerformed(ActionEvent event) {
Component с = (Component)event.getSource();
Dimension d = c.getSizeO;
System.out.println("Size: " + d.width + "x" + d.height);
}
}
13.15. Класс Checkbox 501

Рис. 13.15. С одним компонентом


можно связать несколько объектов
ActionListener

Листинг 13.26. Выходные данные ButtonExample после активизации кнопок


Button 1, Button 2 и Button 3 (кнопки были активизированы в указанной
последовательности)

Foreground: J a v a . a w t . C o l o r [ r = 0 , g = 0 , b = 0 ]
Foreground: j a v a . a w t . C o l o r [ r = 0 , g = 0 , b = 0 ]
Background: j a v a . a w t . C o l o r [ r = 1 2 8,g=12 8,b=12 8]
Foreground: j a v a . a w t . C o l o r [ r = 0 , g = 0 , b = 0 ]
Background: J a v a . a w t . C o l o r [ r = 6 4 , g = 6 4 , b = 6 4 ]
S i z e : 59x23

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


мы создали ддя этой цели класс I m a g e B u t t o n на базе класса C a n v a s . Исходный код
I m a g e B u t t o n доступен по адресу h t t p : //www. c o r e w e b p r o g r a m m i n g . com/.

13.15. Класс Checkbox


В данном разделе описываются флажки опций, которые могут быть установлены не­
зависимо от других элементов того же типа. В следующем разделе мы покажем, как объ­
единять флажки опций в группы, чтобы они могли функционировать как переключате­
ли опций или кнопки с зависимой фиксацией (выбор элемента автоматически отменяет
предыдущий выбор). Как правило, при создании пользовательского интерфейса фла­
жок опции с надписью (текстовой меткой) создается как экземпляр класса Checkbox и
помещается в окно. В процессе работы программы вы можете обрабатывать событие
I t e m S e l e c t i o n E v e n t либо позже, в тот момент, когда сведения о состоянии элемента
будут необходимы, вызвать метод g e t S t a t e . Операции, которые необходимо выпол­
нить для создания элемента и включения его в окно, показаны ниже.
Checkbox cb = new C h e c k b o x ( " . . . " ) ;
somePanel.add(cb);

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

public CheckboxO
public Checkbox(String checkboxLabel)
public Checkbox(String c h e c k b o x L a b e l , b o o l e a n state)
Первый из приведенных конструкторов создает флажок опции без надписи
(текстовой метки). По умолчанию флажок сброшен. Второй конструктор создает
502 Глава 13. Компоненты AWT

элемент Checkbox с указанной надписью. Данный элемент также по умолчанию


сброшен. Для того чтобы задать его состояние, надо использовать метод
s e t S t a t e . Последний конструктор также создает флажок опции с текстовой мет­
кой. Если значение второго параметра равно t r u e , флажок будет установлен, если
значение равно f a l s e — сброшен.

Пример создания флажков опций


в листинге 13.27 создаются двенадцать флажков опций, которые располагаются в
окне в два столбца. Все нечетные флажки установлены изначально. Результат выпол­
нения программы в системе Windows 98 показан на рис. 13.16.

Листинг 13-27. Checkboxes. java

import j a v a . a w t . * ;
public class Checkboxes extends CloseableFrame
public static void main(String[] args) {
new Checkboxes();
}
public Checkboxes 0 {
super("Checkboxes");
setFont(new Font("SansSerif", Font.BOLD, 1£ )) ,
setLayout(new GridLayout(0, 2));
Checkbox box;
for(int i=0; i<12; i++) {
box = new Checkbox("Checkbox " + i ) ;
if (i%2 == 0) {
box.setState(true);
}
add(box);
}
packO ;
setVisible(true);

ШЕШЗШШШШ
И И И И Д И Д ' "'|i"ii 1 |i
F Checkbox p: Г Checkbox 1
F Checkbox 2 Г Checkbox 3
F Checkbox 4 Г Checkbox 5
F Checkbox 6 Г Checkbox 7 Рис. 13.16. Так выглядят
F Checkbox 8 Г Checkbox 9 создаваемые Java-программой
флажки опций в операционной
F Checkbox 10 Г Checkbox 11
системе Windows 98
13.15. Класс Checkbox 503

Методы Checkbox
Н и ж е описаны восемь наиболее часто используемых методов класса C h e c k b o x .

p u b l i c b o o l e a n getState()
public void setState(boolean checkedState)
Метод g e t S t a t e позволяет определить состояние элемента. Если возвращаемое зна­
чение равно t r u e — флажок установлен. Значение f a l s e соответствует сброшенному
флажку. Метод s e t S t a t e позволяет установить либо сбросить флажок опции.

public String getLabel()


public void setLabel(String newLabel)
Метод g e t L a b e l возвращает текущую надпись (метку), связанную с данной кноп­
кой, а метод s e t L a b e l изменяет метку. Как и в случае с кнопками, если флажок
опции уже отображается, изменение метки не приведет к немедленному измене­
нию размеров элемента. Для того чтобы это произошло, надо вызвать методы
i n v a l i d a t e и v a l i d a t e включающего окна, инициировав тем самым процесс
размещения компонентов в контейнере.
s o m e C h e c k b o x . s e t L a b e l ( " А New L a b e l " ) ;
someCheckbox.getParent().invalidate();
someCheckbox.getParent().validate();

public v o i d a d d I t e m L i s t e n e r ( I t e m L i s t e n e r listener)
p u b l i c v o i d r e m o v e I t e m L i s t e n e r ( I t e m L i s t e n e r listener)
Метод a d d l t e m L i s t e n e r связывает объект I t e m L i s t e n e r с флажком опции, а
метод r e m o v e I t e m L i s t e n e r разрывает эту связь. Использование данных методов
мы обсудим ниже.

public v o i d p r o c e s s I t e m E v e n t ( I t e m E v e n t event)
Данный метод предназначен для низкоуровневой обработки событий. Используя
метод p r o c e s s I t e m E v e n t , надо разрешить пер>едачу событий и включить в тело
метода вызов s u p e r . p r o c e s s I t e m E v e n t .

public Object[ ] g e t S e l e c t e d O b j e c t s O
Данный метод возвращает массив, состоящий из одного элемента. В массиве со­
держится либо метка (если флажок установлен), либо значение n u l l (если флажок
сброшен).
Подобно прочим элементам графического интерфейса, C h e c k b o x наследует все
методы класса Component.

Поддержка событий Checkbox


Элементы C h e c k b o x не генерируют события A c t i o n E v e n t , вместо этого они вы­
ступают как источники I t e m S e l e c t i o n E v e n t . Для того чтобы сам элемент мог обра­
ботать события, надо разрешить их передачу.
504 Глава 13. Компоненты AWT

enableEvents(AWTEvent.ITEM EVENT MASK);


После этого необходимо переопределить метод p r o c e s s I t e m E v e n t , которому в
качестве параметра передается объект I t e m E v e n t . Если с данным объектом связаны
обработчики I t e m L i s t e n e r , надо в теле p r o c e s s I t e m E v e n t вызвать s u p e r ,
p r o c e s s I t e m E v e n t . В дополнение к методам, определенным в классе AWTEvent,
I t e m E v e n t содержит два важных метода: g e t l t e m S e l e c t a b l e и g e t S t a t e C h a n g e .
Метод g e t l t e m S e l e c t a b l e возвращает флажок опции как объект I t e m S e l e c t a b l e
(интерфейс I t e m S e l e c t a b l e реализует C h e c k b o x ) . Метод g e t S t a t e C h a n g e воз­
вращает либо ItemEvent.SELECTED, либо ItemEvent.DESELECTED.
Для того чтобы обрабатывать события посредством другого объекта, надо с помо­
щью метода a d d l t e m L i s t e n e r связать с флажком опции объект I t e m L i s t e n e r .
В классе I t e m L i s t e n e r должен быть реализован метод i t e m S t a t e C h a n g e d , которо­
му в качестве параметра передается объект I t e m E v e n t .

13.16. Переключатели опций


Если вы объедините объекты C h e c k b o x в группу с помощью C h e c k b o x G r o u p , то
получите переключатели опций, которые также называют кнопками с зависимой
фиксацией. В группе может быть установлен только один переключатель; выбор но­
вого элемента автоматически отменяет предыдущий выбор. Внешний вид переключа­
телей опций также отличается от графического представления флажков опций. Как и
при работе с HTML-формами (они были описаны в главе 1), переключатели опций,
входящие в одну группу, не обязательно должны располагаться рядом друг с другом.
Однако в большинстве случаев разработчики размещают данные элементы недалеко
один от другого, объединяя их в контейнере P a n e l . Как правило, при использовании
переключателей опций в программе сначала создается объект C h e c k b o x G r o u p , а за­
тем — объекты C h e c k b o x , принадлежащие группе. В конструкторе C h e c k b o x следует
указать, должен ли компонент быть выбран по умолчанию. Код, предназначенный для
создания группы переключателей опций, выглядит следующим образом.
CheckboxGroup cbGroup = new C h e c k b o x G r o u p ( ) ;
Checkbox c b l = new C h e c k b o x ( " . . . " , c b G r o u p , t r u e ) ;
add(cbl);
Checkbox cb2 = new C h e c k b o x ( " . . . " , c b G r o u p , f a l s e ) ;
add(cb2);

Конструкторы
в данном разделе описываются конструкторы C h e c k b o x G r o u p и C h e c k b o x .

CheckboxGroup
public CheckboxGroupO
Данный конструктор создает объект C h e c k b o x G r o u p , который не имеет графиче­
ского представления, а лишь объединяет элементы C h e c k b o x в одну группу.
Внешний вид элементов C h e c k b o x , объединенных с помощью C h e c k b o x G r o u p ,
также изменяется. В каждый момент времени может быть выбран лишь один эле­
мент в группе.
13.16. Переключатели опций 505

Checkbox
public Checkbox(String label, CheckboxGroup group, boolean state)
public Checkbox(String label, boolean state, CheckboxGroup group)
Первый из приведенных конструкторов создает элемент переключателя, связан­
ный с указанной группой и помеченный заданной кнопкой. Параметр s t a t e опре­
деляет состояние компонента по умолчанию. Если значение параметра s t a t e
равно t r u e для нескольких элементов в группе, выбирается последний из этих
элементов. Второй конструктор выполняет те же функции, что и первый, но по­
рядок следования параметров в нем изменен.

Пример использования флажков и переключателей


опций
в листинге 13.28 приведен код аплета, который демонстрирует различия между
обычными флажками опций и элементами Checkbox, объединенными с помощью
CheckboxGroup. В левом столбце расположены переключатели опций, в правом —
флажки опций. На рис. 13.17 показаны результаты выполнения аплета в системе
Windows 98.

Листинг 13.28.CheckboxGroups.Java

import java.applet.Applet;
import j ava.awt.*;

public class CheckboxGroups extends Applet {


public void initO {
setLayout(new GridLayout(4, 2));
setBackground(Color.lightGray);
setFont(new Font("Serif", Font.BOLD, 16));
add(new Label("Flavor", Label.CENTER));
add(new Label("Toppings", Label.CENTER));
CheckboxGroup flavorGroup = new CheckboxGroup();
add(new Checkbox("Vanilla", flavorGroup, true));
add(new Checkbox("Colored Sprinkles"));
add(new Checkbox("Chocolate", flavorGroup, false))
add(new Checkbox("Cashews"));
add(new Checkbox("Strawberry", flavorGroup, false)
add(new Checkbox("Kiwi"));
}
}
506 Глава 13. Компоненты AWT

i ^ A p p l e t Vie«№t: Checkboxfitaupt.oias» :::^ГУЭ:'Ш:ШШ&


Apptet

Flavor tm^ у^^^ц:


F|Coioreass«bidej| .- -
^ Giocolate FCasbews

С Strawbeny ГШда
Рис. 13.17. Переключатели опций (слева)
и флажки опций (справа), отображаемые в
Applet started.
системе Windows 98

Методы классов CheckboxGroup и Checkbox

CheckboxGroup
public Checkbox getSelectedCheckbox()
Метод g e t S e l e c t e d C h e c k b o x возвращает выбранный в данный момент пере­
ключатель опции (Checkbox). Если ни один из переключателей не выбран, воз­
вращается значение n u l l .

public void setSelectedCheckbox(Checkbox boxToSelect)


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

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

public CheckboxGroup getCheckboxGroupO


Метод g e t CheckboxGroup определяет группу, к которой принадлежит данный
переключатель.

public void setCheckboxGroup(CheckboxGroup newGroup)


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

Поддержка событий CheckboxGroup


Включение компонента Checkbox в группу не изменяет порядок обработки собы­
тий, описанный в предыдущем разделе. Однако при работе с переключателями опций
существует одна особенность. При использовании флажков опций вы можете отка­
заться от обработки событий и непосредственно проверять состояние флажка по­
средством g e t S t a t e . Для переключателей опций также можно организовать цикл и
опрашивать в нем элементы группы с помощью метода g e t S t a t e , но такой подход не
оправдан. Гораздо удобнее вызвать метод g e t S e l e c t e d C h e c k b o x объекта Check­
boxGroup, который возвращает ссылку на выбранный элемент Checkbox.
13.17. Элемент Choice 507

13.17. Элемент Choice


Элемент C h o i c e реализует раскрывающийся список. В окне отображается вы­
бранный в данный момент пункт списка. После щелчка мышью на кнопке со стрелкой
отображаются другие пункты списка, доступные для выбора. В отличие от кнопок и
флажков опций, элемент C h o i c e не создается полностью в теле конструктора. Про­
цесс создания раскрывающегося списка состоит из двух этапов. На первом этапе соз­
дается пустой список, а на втором этапе к списку добавляются требуемые пункты. Для
создания элемента C h o i c e и включения его в конструктор используется код, подоб­
ный приведенному ниже.
Choice choice = new Choice();
choice.addltem("...");
choice.addltem("...");

somePanel.add(choice) ;

Конструктор
в icTiacce C h o i c e определен единственный конструктор, используемый по умолча­
нию.
public Choice()
Данный конструктор создает пустой раскрывающийся список. Пункты списка до­
бавляются с помощью метода a d d .

Пример раскрывающегося списка


в листинге 13.29 показан аплет, в котором создается элемент Choice, содержащий
три пункта. Результаты выполнения данного кода в системе Windows 98 показаны на
рис. 13.18.

Листинг 13.29.ChoiсеТвSt.Java

import java.applet.Applet;
import java.awt.*;

public class ChoiceTest extends Applet {


private Choice choice;

public void init{) {


setFont(new Font("SansSerif", Font.BOLD, 36))
choice = new Choice();
choice.addltem("Choice 1")
choice.addltem("Choice 2")
choice.addltem("Choice 3")
add(choice);
}
508 Глава 13. Компоненты AWT

[Choice 1 Д

IChoice 2 1 j
Ichoice 3 1 1

i Applet started. 1

Рис. 13.18. Внешний вид элемента c h o i c e в системе Windows 98:


а - - исходное состояние элемента; б — список раскрыт

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


Ниже перечислены методы класса Choice, доступные разработчику.

public void addItem(String menultem)


public void add(String menultem)
Раскрывающийся список создается в два этапа. Сначала с помощью конструктора
создается пустой список. Далее посредством метода addltem или add к списку до­
бавляются пункты. По умолчанию первый пункт, включенный в список, выбирается
и отображается в окне, однако вы можете выбрать другой пункт, вызвав метод
s e l e c t . Для удаления пунктов и списка используется метод remove либо
removeAll.

public void addItemLListener(ItemListener listener)


public void removeItemListener(ItemListener listener)
Метод a d d l t e m L i s t e n e r связывает с объектом обработчик I t e m L i s t e n e r , а ме­
тод remove I t e m L i s t e n e г разрывает эту связь.

public int getItemCount()


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

public String getltem(int itemlndex)


Метод g e t I tern возвращает метку указанного пункта списка.

public int getSelectedlndexO


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

public String getSelectedltemO


Этот метод возвращает метку выбранного пункта списка.
13.17. Элемент Choice 509

public Object[ ] getSelectedObjectsO


Метод g e t S e l e c t e d O b j e c t s возвращает массив, содержащий выбранный пункт
списка. Если ни один из пунктов не выбран, возвращается значение n u l l .

public void insert(String menultem, int itemlndex)


Метод i n s e r t включает очередной пункт в указанную позицию списка. Данный
метод отличается от add и addltem, которые добавляют пункт в конец списка.

public void processItemEvent(Item£vent event)


Если вы хотите, чтобы объект Choice выполнял низкоуровневую обработку соб­
ственных событий, необходимо переопределить метод p r o c e s s I t e m E v e n t .

public void remove(String menultem)


public void remove(int itemlndex)
public void removeAll()
Данные методы предназначены для удаления пунктов списка.

public void select(int itemlndex)


public void select(String itemLabel)
Первый из указанных методов выбирает пункт списка по заданному индексу. Как
вы знаете, в Java номера индексов начинаются с нуля. Метод s e l e c t ( S t r i n g
itemLabel) выбирает первый пункт списка, метка которого соответствует ука­
занной. Если заданная метка отсутствует, метод не выполняет никаких действий.
Как и для других компонентов, методы, определяющие цвет, шрифт, размеры и дру­
гие характеристики раскрывающегося списка, наследуются от объекта Component.

Поддержка событий, связанных с объектом Choice


Обработка событий, связанных с объектом Choice, происходит подобно обработ­
ке событий Checkbox: разработчик переопределяет p r o c e s s ItemEvent либо ис­
пользует a d d l t e m L i s t e n e r для связывания обработчика с объектом. Основное от­
личие состоит в том, что флажок опции находится в одном из двух состояний, а для
раскрывающегося списка таких состояний может быть несколько. Поэтому, опреде­
лив, что источником события стал раскрывающийся список, надо выяснить, какой из
пунктов был выбран. В листинге 13.30 показана расширенная версия рассмотренного
ранее класса ChoiceTest. При обработке событий выводятся данные о выбранном
пункте. Как и в случае с флажками опций, разработчики часто предпочитают не обра­
батывать прерывания, а проверять состояние компонента с помощью методов
g e t S e l e c t e d i t e m или g e t S e l e c t e d l n d e x .

Листинг 13.30.ChoiceTest2.Java

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
510 Глава 13. Компоненты AWT

import Java.awt.event.*;
public class ChoiceTest2 extends Applet
implements ItemListener {
private Choice choice;
public void initO {
setFont(new Font("SansSerif", Font.BOLD, 36));
choice = new Choice();
choice.addltem("Choice 1");
choice.addltem("Choice 2 " ) ;
choice.addltem("Choice 3");
choice.addltemListener(this);
add(choice);
}
public void itemStateChanged(ItemEvent event) {
Choice choice = (Choice)event.getSource();
String selection = choice.getSelectedltem();
if (selection.equals("Choice 1")) {
doChoicelAction();
} else if (selection.equals("Choice 2")) {
doChoice2Action();
} else if (selection.equals("Choice 3")) {
doChoice3Action();
}

private void doChoicelAction() {


System.out.println("Choice 1 Action");

private void doChoice2Action() {


System.out.println("Choice 2 Action");
}
private void doChoice3Action() {
System.out.println("Choice 3 Action");

13.18. Элемент List


Окно списка (элемент L i s t ) представляет прокручиваемый список, в котором
пользователь может выбрать один или несколько пунктов. Как и в случае элемента
Choice, создание окна списка происходит в два этапа: сначала создается пустое окно,
а затем в него добавляются пункты списка.
L i s t l i s t = new L i s t ( ) ;
list.addltem("...");
list.addltem("...");

add(list);
13.18. Элемент List 511

Конструкторы
в классе L i s t предусмотрены три конструктора.

public List()
public List(int rows)
public List(int rows, b o o l e a n multiSelectable)
Первый конструктор создает окно списка, допускающее выбор лишь одного пунк­
та. Ш и р и н а и высота элемента зависят от платформы. Второй конструктор созда­
ет окно списка с заданным числом строк. Как и в предыдущем случае, созданный
элемент допускает выбор только одного пункта списка.
Последний из представленных выше конструкторов создает окно списка, содер­
жащее указанное число строк. Число строк определяет высоту элемента, но не
максимальное число пунктов списка. Если все пункты списка не помещаются в ок­
не, автоматически создается полоса прокрутки. Второй параметр указывает, дол­
жен ли элемент допускать выбор нескольких пунктов списка. Как и ддя элемента
C h o i c e , конструктор L i s t создает пустое окно; пункты списка добавляются по­
средством метода a d d l t e m или add. Ш и р и н а элемента зависит от платформы и,
как правило, не определяется максимальными размерами пункта списка. П р и на­
писании программы вы можете явным образом указать размеры элемента (если
используемый диспетчер компоновки разрешит сделать это).

Примеры элементов List


в листинге 13.31 приведен код аплета, в котором создаются два элемента L i s t .
Первый элемент допускает выбор лишь одного пункта списка, а второй позволяет вы­
бирать несколько пунктов. Результаты выполнения программы в системе Windows 98
показаны на рис. 13.19.

Листинг 1 3 . 3 1 . L i s t s . J a v a

import java.awt.*;
public class Lists extends CloseableFrame {
public static void main(String[] args) {
new Lists();

public Lists() {
super("Lists");
setLayout(new FlowLayout());
setBackground(Color.lightGray);
setFont(new Font("SansSerif", Font.BOLD, 18));
List listl = new List(3, false);
listl.add("Vanilla") ;
listl.add("Chocolate");
listl.add("Strawberry");
add(listl);
512 Глава 13. Компоненты AWT

List list2 s new List(3, true);


list2.add("Colored Sprinkles");
list2.add("Cashews");
list2.add("Kiwi");
add(list2);
packO;
setVisible(true);

Vanilla Colored Sprinklesg


ichocolate Cashews
Рис. 13.19. Окна списков, отображаемые
в системе Windows 98

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


Н и ж е описаны 28 методов, которые доступны разработчику, использующему
класс L i s t .

public v o i d add(String itemLabel)


public v o i d add(String itemLabel, int i t e m l n d e x )
Первый из приведенных методов добавляет пункт с указанной меткой к концу спи­
ска. Второй метод включает пункт в заданную позицию списка; все пункты с индек­
сом, равным указанному или превышающим его, сдвигаются вниз.

public v o i d addActionListener(ActionListener listener)


public v o i d a d d I t e m L i s t e n e r ( I t e m L i s t e n e r listener)
public v o i d r e m o v e A c t i o n L i s t e n e r ( A c t i o n L i s t e n e r listener)
public v o i d r e m o v e I t e m L i s t e n e r ( I t e m L i s t e n e r listener)
Данные методы позволяют связывать с объектом A c t i o n L i s t e n e r либо I t e m -
L i s t e n e r и разрывать связь. Событие I t e m E v e n t генерируется при выборе и от­
мене выбора пункта. Событие A c t i o n E v e n t генерируется тогда, когда пользова­
тель дважды щелкает мышью на пункте списка либо нажимает клавишу <Enter>,
когда пункт списка выбран. Данные методы применимы только к объектам L i s t ,
допускающим выбор одного пункта списка.

public b o o l e a n isMultipleMode()
Данный метод позволяет определить, допускает ли элемент выбор одного ( f a l s e )
либо нескольких ( t r u e ) пунктов списка.

public void removeAllO


Этот метод удаляет из списка все пункты.

public int g e t l t e m C o u n t O
Метод g e t l t e m C o u n t возвращает число пунктов в списке.
13.18. Элемент List 513

public void remove(String m e n u l t e m )


public void remove(int i t e m l n d e x )
Данные методы удаляют указанные пункты из списка.

public void d e s e l e c t ( i n t i t e m l n d e x )
Если пункт, соответствующий указанному индексу, выбран, метод d e s e l e c t отме­
няет его выбор. Если пункт не выбран, никакие действия не выполняются.

public String g e t l t e m ( i n t i t e m l n d e x )
public String[] g e t l t e m s O
Первый из приведенных методов возвращает метку, соответствующую пункту в
указанной позиции. Второй метод возвращает массив меток в порядке следования
пунктов.

public int getRowsO


Данный метод возвращает число строк в окне списка, которое не изменяется за
время существования элемента и определяет высоту окна. Не следует путать этот
метод с методом g e t l t e m C o u n t , который возвращает число пунктов в списке, ко­
торое может изменяться. Если количество пунктов списка превышает число строк,
к окну автоматически добавляется полоса прокрутки.

public int g e t S e l e c t e d l n d e x O
public int[] g e t S e l e c t e d I n d e x e s ( )
Для элемента, допускающего выбор только одного пункта списка, первый метод по­
зволяет определить индекс выбранного пункта. Если ни один пункт не выбран либо
если элемент допускает выбор нескольких пунктов, возвращается значение -1. Вто­
рой метод возвращает массив индексов выбранных пунктов. Если ни один пункт не
выбран, возвращается массив нулевой длины (но не значение n u l l ) .

public String g e t S e l e c t e d l t e m O
public String[ ] g e t S e l e c t e d I t e m s ( )
Для элемента, допускающего выбор только одного пункта списка, первый метод
позволяет определить метку, соответствующую выбранному пункту. Если ни один
пункт не выбран либо если элемент допускает выбор нескольких пунктов, возвра­
щается значение n u l l . Второй метод возвращает массив индексов выбранных
пунктов. Он может использоваться как при работе с элементами, допускающими
выбор одного пункта, так и с элементами, позволяющими выбирать несколько
пунктов. Если ни один пункт не выбран, возвращается массив нулевой длины (но
не значение n u l l ) .

public Object[ ] g e t S e l e c t e d O b j e c t s ( )
Метод g e t S e l e c t e d O b j e c t s возвращает массив меток, соответствующих вы­
бранным пунктам. Если ни один пункт не выбран, возвращается значение n u l l .
514 Глава 13. Компоненты AWT

public int getVisiblelndexO


Данный метод возвращает индекс пункта, который был указан при последнем вы­
зове m a k e V i s i b l e . Если метод m a k e V i s i b l e раньше не вызывался, возвращается
нулевое значение.

public boolean isIndexSelected(int itemlndex)


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

public void makeVisible(int itemlndex)


Метод m a k e V i s i b l e прокручивает список так, что пункт, индекс которого указан
в качестве параметра, попадает в область видимости.

public void processItemEvent(ItemEvent event)


public void processActionEvent(ActionEvent event)
Если вы хотите, чтобы элемент L i s t обрабатывал собственные события на низком
уровне, вам надо переопределить один или оба указанных метода. Событие
ItemEvent возникает при каждом выборе или отмене выбора пункта. ActionEvent
генерируется после двойного щелчка мышью на пункте списка либо после нажатия
клавиши <Enter> в тот момент, когда соответствующий пункт списка выбран.

public void replaceItem(String newItemLabel, int itemlndex)


Данный метод заменяет пункт списка, соответствующий указанному индексу.

public void select(int itemlndex)


Этот метод выбирает пункт списка, индекс которого указан в качестве параметра.
Если элемент не позволяет выбирать несколько пунктов списка, то выбор ранее
выбранного пункта отменяется. В отличие от элемента Choice, в классе L i s t от­
сутствует вариант метода s e l e c t , который позволят выбирать пункт по его метке.

public void setMultipleMode(boolean multipleSelectable)


Метод setMultipleMode разрешает ( t r u e ) или запрещает ( f a l s e ) выбор не­
скольких пунктов списка.
Поскольку класс L i s t является подклассом класса Component, он также наследует
все методы своего суперкласса.

Поддержка событий элемента List


Элемент L i s t интересен тем, что с ним связаны два типа событий. Событие
I t e m S e l e c t i o n E v e n t генерируется в ответ на выбор или отмену пункта пользовате­
лем. Заметьте, что вызов из программы метода s e l e c t не приводит к возникновению
события данного типа. Событие ActionEvent возникает только при работе с эле­
ментами L i s t , допускающими выбор одного пункта списка. Данное событие генери­
руется тогда, когда пользователь дважды щелкнет мышью на пункте списка либо при
13.18. Элемент List 515

выбранном пункте списка нажмет клавишу <Enter>. Объекты L i s t также получают


все события, имеющие отношение к действиям с мышью и клавиатурой, а также свя­
занные с получением и потерей фокуса ввода.
Если вы хотите, чтобы объект L i s t самостоятельно обрабатывал собственные со­
бытия, вам надо разрешить их, используя маски AWTEvent. ITEM_EVENT_MASK и
AWTEvent. ACTION_EVENT_MASK, а затем переопределить методы p r o c e s s l t e m -
E v e n t и p r o c e s s A c t i o n E v e n t . Вы также можете организовать обработку событий
другим объектом; такой подход обеспечивает большую гибкость. Для этого надо заре­
гистрировать обработчик I t e m L i s t e n e r посредством метода a d d l t e m L i s t e n e r , а
обработчик A c t i o n L i s t e n e r — с помощью a d d A c t i o n L i s t e n e r . Пример реализа­
ции второго подхода приведен в листинге 13.32. При этом используются вспомога­
тельные классы, представленные в листингах 13.33 (этот класс реализует интерфейс
I t e m L i s t e n e r ) и 13.34 (приведенный в нем класс реализует интерфейс A c t i o n -
L i s t e n e r ) . Результаты выполнения приложения показаны на рис. 13.20.

Листинг 1 3 . 3 2 . L i s t E v e n t s . J a v a

import java.awt.*;
import Java.awt.event.*;

/** К л а с с , демонстрирующий выбор и отмену выбора


* пунктов с п и с к а , а также о б р а б о т к у событий
* ActionEvent.
Ч

public class ListEvents extends CloseableFrame {


public static void main(String[] args) {
new ListEvents();

protected List languageList;


private TextField selectionField, actionField;
private String selection = "[NONE]", action;

/** Создание окна Frame, содержащего список языков


* программирования и два поля для редактирования.
* В одном поле отображается последний выбранный пункт,
* а в другом - последний активизированный пункт списка.
V
public ListEventsО {
super("List Events");
setFont(new Font("Serif", Font.BOLD, 16));
add(makeLanguagePanel(), BorderLayout.WEST);
add(makeReportPanel(), BorderLayout.CENTER);
packO ;
setVisible(true);

// Создание панели, содержащей объект List.


// Конструктор размещает панель в левой части
// окна Frame.

private Panel makeLanguagePanel() {


516 Глава 13. Компоненты AWT

Panel languagePanel = new Panel();


languagePanel.setLayout(new BorderLayout());
languagePanel.add(new Label("Choose Language"),
BorderLayout.NORTH);
languageList = new List(3);
String[] languages =
{ "Ada", "C", "C++", "Common Lisp", "Eiffel",
"Forth", "Fortran", "Java", "Pascal",
"Perl", "Scheme", "Smalltalk" };
for(int i=0; Klanguages. length; i++) {
languageList.add(languages[i]);
}
showJava();
languagePanel.add("Center", languageList);
return(languagePanel);
}
// Создание панели с двумя объектами Label и двумя
// полями для редактирования. В первом поле редактирования
// отображается элемент, который был выбран в списке
// последним. Во втором поле редактирования показан
// последний активизированный элемент. Конструктор
// помещает объект Panel в правую часть окна Frame.
private Panel makeReportPanel() {
Panel reportPanel = new Panel();
reportPanel.setLayout(new GridLayout(4, 1));
reportPanel.add(new Label("Last Selection:"));
selectionField = new TextFieldO;
SelectionReporter selectionReporter =
new SelectionReporter(selectionField);
languageList.addltemListener(selectionReporter);
reportPanel.add(selectionField);
reportPanel.add(new Label("Last Action:"));
actionField = new TextFieldO;
ActionReporter actionReporter =
new ActionReporter(actionField);
languageList.addActionListener(actionReporter);
reportPanel.add(actionField);
return(reportPanel);
}

/** Выбор и отображение пункта "Java". */

protected void showJava() {


languageList.select (7);
languageList.makeVisible(7);
}
}

Листинг 13.33.SelectionReporter.Java

import java.awt.^;
import Java.awt.event.
13.18. Элемент List 517

/** Выбранный пункт отображается в поле редактирования,


* которое было передано конструктору SelectiorxReporter.

public class SelectlonReporter implements ItemListener {


private TextField selectionField;

public SelectionReporter(TextField selectionField) {


this.selectionField = selectionField;
}
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == event.SELECTED) {
List source = (List)event.getSource();
selectionField.setText(source.getSelectedItem());
} else
selectionField.setText("");
}

Листинг 13.34. ActionReporter. Java

import j ava.awt.*;
import Java.awt.event.^;

f-k-k Активизированный пункт отображается в поле редактирования,


^ которое было передано конструктору ActionReporter.

public class ActionReporter implements ActionListener


private TextField actionField;

public ActionReporter(TextField actionField) {


this.actionField = actionField;
}
public void actionPerformed(ActionEvent event) {
List source = (List)event.getSource();
actionField.setText(source.getSelectedltem());
}

^List Event»
Choose Language Last Selection:
±J Java
Pascal
Last Action:
Perl Рис. 13.20. Внешний вид приложения после двойного щелчка
Scheme ^Pascal
на пункте "Pascal" и одного щелчка на пункте "Java"
518 Глава 13. Компоненты AWT

Объектам List передаются события, связанные с клавиатурой, м ы ш ь ю и фокусом


ввода. В листинге 13.35 приведен модифицированный код предыдущего примера, в
котором при вводе пользователем любой буквы из слова "Java" (в верхнем или нижнем
регистре) выбирается и становится видимым пункт "Java". Заметьте, что для дости­
жения данного результата используется внутренний класс. Вместо отдельного класса
KeyAdapter создается встроенная версия. Данный подход очень удобен, кроме того,
благодаря ему обеспечивается доступ к методу show Java класса Li stE vents, объяв­
ленному как protected.

Листинг 13.35.ListEvents2.Java

import Java.awt.event.*;
public class ListEvents2 extends ListEvents {
public static void main(String[J-args) {
new ListEvents2();
}
/** Расширенный класс ListEvents. Ввод любого
* символа, входящего в состав "JAVA" или "Java",
* приводит к выбору пункта "Java".

public ListEvents2 () {
super ();
// Создание KeyAdapter и связывание его с languageList.
// Поскольку обработчик является внутренним классом,
// он имеет доступ ко всем данным и методам, в частности
// к методу showJava класса ListEvent.
KeyAdapter javaChooser = new KeyAdapter() {
public void keyPressed(KeyEvent event) {
int key = event.getKeyChar0;
if ("JAVAjava".indexOf(key) != -1) {
showJava{);

languageList.addKeyListener(javaChooser)
}
}

13.19. Класс TextField


Объект T e x t F i e l d отображает поле, предназначенное для представления и ре­
дактирования строки текста. Для работы с несколькими строками текста использует­
ся элемент TextArea, который будет рассмотрен в следующем разделе. Если элемент
T e x t F i e l d предполагается использовать для ввода или редактирования текста, при
его создании обычно задаются размеры либо он инициируется текстовой строкой.
13.19. Класс TextField 519

TextField lastNameField = new TextField(15) ;


add(lastNameField);
TextField langField = new TextField("Java");
add(langField);
Чтобы элемент TextField допускал только отображение текста, надо отключить
функции ввода, вызвав метод set Editable. Часто поле заполняется уже после его
создания.
TextField temperatureField = new TextField(4);
temperatureField.setEditable(false);
statusPanel.add(temperatureField) ;

temperatureString = simulationTemperature("F");
temperatureField.setText(temperatureString);

Конструкторы
в классе T e x t F i e l d определено четыре конструктора.

p u b l i c TextFieldO
public TextField(int numChars)
p u b l i c TextField(String initialString)
public TextField(String initialString, int numChars)
Первый из приведенных здесь конструкторов создает пустое поле редактирования,
ширина которого зависит от операционной системы (обычно она равна одному сим­
волу). Второй конструктор создает пустое поле редактирования, ширина которого
достаточна для размещения указанного числа символов. Заметьте, что при использо­
вании пропорциональных шрифтов различные символы имеют разные горизонталь­
ные размеры, поэтому для вычисления ширины элемента используется усредненная
ширина символа. Третий конструктор создает поле редактирования, инициализиро­
ванное указанной строкой. Ширина его достаточна для того, чтобы разместить данную
строку. Последний из конструкторов создает элемент указанной ширины, инициали­
зированный заданной строкой. Ширина поля редактирования вычисляется на основе
усредненных размеров символов, отображаемых конкретным шрифтом.

Пример создания полей редактирования


в листинге 13.36 приведен код аплета, который создает поля редактирования, ис­
пользуя один из рассмотренных выше конструкторов. Результаты выполнения аплета
показаны на рис. 13.21.

Листинг 1 3 . 3 6 . Т е х t F i e l d s . J a v a

import Java.applet.Applet;
import java.awt.*;
/** Компоненты T e x t F i e l d , с о з д а в а е м ы е каждым и з четырех
к о н с т р у к т о р о в */
520 Глава 13. Компоненты AWT

public class TextFields extends Applet {


public void init() {
add(new TextField());
add(new TextField(30));
add(new TextField("Initial String"));
add(new TextField("Initial", 30));
}
}

^iQjjdj
Apptet

Г
1
Initial String

; j Initial
Рис. 13.21. Элементы T e x t F i e l d , созданные каждым
Applet started.
из четырех конструкторов

Методы TextField
Класс T e x t F i e l d является подклассом класса T e x t C o m p o n e n t . Н и ж е будут описа­
ны методы обоих классов. Чаще всего используются методы g e t T e x t и s e t T e x t
(получение и установка содержимого T e x t F i e l d ) , s e t E d i t a b l e (разрешение и за­
прещение ввода текста), g e t C o l u m n s и s e t C o l u m n s (определение и установка ши­
р и н ы элемента T e x t F i e l d ) и s e t E c h o C h a r (отображение знака "*" либо другого
символа при вводе текста; обычно используется при вводе пароля).

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


p u b l i c v o i d a d d T e x t L i s t e n e r ( T e x t L i s t e n e r listener)
public v o i d r e m o v e T e x t L i s t e n e r ( T e x t L i s t e n e r listener)
Метод a d d T e x t L i s t e n e r связывает объект с обработчиком T e x t L i s t e n e r , а метод
r e m o v e T e x t L i s t e n e r разрывает эту связь. Как вы помните, в классе Component,
рассмотренном в начале данной главы, определены методы a d d K e y L i s t e n e r и
r e m o v e K e y L i s t e n e r , но в этом случае события генерируются при любом измене­
нии текста, даже если это изменение выполнено по инициативе программы. Объект
T e x t L i s t e n e r должен реализовывать единственный метод t e x t V a l u e C h a n g e d .
В качестве параметра данному методу передается объект T e x t E v e n t . Для получения
строки, содержащейся в поле редактирования, сначала вызывается метод g e t -
S o u r c e , с помощью которого определяется источник события. Данный объект при­
водится к типу T e x t C o m p o n e n t , и вызывается метод g e t T e x t .

public int getCaretPosition()


public v o i d setCaretPosition(int i n d e x )
Первый из указанных методов используется для определения позиции текстового
курсора, а второй задает положение курсора.
13.19. Класс TextField 521

public String getSelectedTextO


Данный метод возвращает выделенный фрагмент текста. Если текст не выделен,
возвращается строка нулевой длины (но не объект n u l l ) .

public int getSelectionEnd()


public void setSelectionEnd(int startlndex)
Метод g e t S e l e c t i o n E n d возвращает индекс первого символа, расположенного
после окончания выделенного фрагмента. Если текст не выделен, возвращается
нл'левое значение. Метод s e t S e l e c t i o n E n d задает индекс первого символа после
выделенного текста.

public int getSelectionStart()


public void setSelectionStart(int endlndex)
Метод g e t S e l e c t i o n S t a r t возвращает индекс первого символа выделенного
фрагмента. Если текст не выделен, возвращается нулевое значение. Метод
s e t S e l e c t i o n S t a r t задает индекс первого символа выделенного текста.

public String getTextO


public void setText(String newText)
Метод g e t T e x t возвращает текст, содержащийся в поле редактирования. Если по­
ле пусто, возвращается строка нулевой длины (но не значение n u l l ) . Метод
s e t T e x t заменяет текст, который содержится в поле редактирования, заданной
строкой. Значение параметра n u l l имеет тот же смысл, что и значение " ".

public boolean isEditable()


public void setEditable(boolean editableStatus)
Метод i s E d i t a b l e позволяет определить, разрешен ( t r u e ) или запрещен
( f a l s e ) ввод текста. Метод s e t E d i t a b l e разрешает ( t r u e ) или запрещает
( f a l s e ) вводить текст в поле редактирования.

public void processTextEvent(TextEvent event)


Для того чтобы элемент обрабатывал события на низком уровне, метод р г о с е s s -
TextEvent должен быть переопределен. Вы также можете поддерживать собы­
тия, связанные с клавиатурой, поскольку TextComponent наслед)'ет от класса
Component метод processKeyEvent. Создавая программу, не забудьте разрешить
текстовые события, импортировать пакет j ava . awt. e v e n t (import j ava . awt.
e v e n t . * ;) и включить в тело метода вызов s u p e r . p r o c e s s T e x t E v e n t .

public void select(int startlndex, int endlndex)


Данный метод выделяет фрагмент текста, начиная с индекса s t a r t l n d e x и закан­
чивая символом, непосредственно расположенным перед endlndex. В большин­
стве операционных систем выделенный текст подсвечивается. Если значение
endlndex превышает длину строки, выделяется весь текст, содержащийся в поле
редактирования.
522 Глава 13. Компоненты AWT

public void selectAll()


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

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


в дополнение к унаследованным от T e x t C o m p o n e n t в классе T e x t F i e l d опреде­
лены следующие методы.

public v o i d addActionListener(ActionListener listener)


public void r e m o v e A c t i o n L i s t e n e r ( A c t i o n L i s t e n e r listener)
Событие A c t i o n E v e n t генерируется, если пользователь нажимает клавишу
<Return> в тот момент, когда объект T e x t F i e l d обладает фокусом ввода. Метод
a d d A c t i o n L i s t e n e r связывает указанный обработчик с объектом, а метод
r e m o v e A c t i o n L i s t e n e r разрывает установленную связь. Следует заметить, что
на практике методы a d d T e x t L i s t e n e r и r e m o v e T e x t L i s t e n e r (унаследованные
от класса T e x t C o m p o n e n t ) , а также методы a d d K e y L i s t e n e r и r e m o v e K e y -
L i s t e n e r (унаследованные от класса Component) применяются гораздо чаще.

public b o o l e a n echoCharIsSet()
Данный метод позволяет определить, был ли установлен эхо-символ (см. описание
метода s e t E c h o C h a r ) .

public int getColumnsO


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

public char getEchoChar()


public void setEchoChar(char echoChar)
Метод g e t E c h o C h a r возвращает текущий эхо-символ. Данное понятие имеет
смысл только в том случае, если значение e c h o C h a r I s S e t равно t r u e . Метод
s e t E c h o C h a r задает эхо-символ — символ, отображаемый в поле редактирования в
ответ на ввод любого знака. Реально введенный текст дост)^'пен с помощью метода
g e t T e x t . Чаще всего эхо-символ используется при вводе пароля и его роль выпол­
няется знак "*".

public void processActionEvent(ActionEvent event)


Событие A c t i o n E v e n t генерируется в ответ на нажатие пользователем клавиши
<Enter> при наличии фокуса ввода у поля редактирования. Если вы хотите, чтобы
объект T e x t F i e l d самостоятельно обрабатывал свои события, вам надо разре­
шить их, используя маску AWTE v e n t .ACT ION_EVENT_MASK, а затем переопреде­
лить метод p r o c e s s A c t i o n E v e n t . Если с объектом связаны обработчики, в тело
переопределенного метода необходимо включить вызов s u p e r . p r o c e s s A c t i o n ­
E v e n t (см. также описание метода a d d A c t i o n L i s t e n e r ) .
13.20. Класс TextArea 523

public void s e t C o l u m n s ( i n t cols)


Метод s e t C o l u m n s позволяет непосредственно задавать ширину элемента
T e x t F i e I d . Заметьте, что ширина поля редактирования вычисляется из усред­
ненной ш и р и н ы символа, отображаемого конкретным шрифтом.
Поскольку T e x t F i e l d является подклассом класса Component, он имеет доступ к
методам своего суперкласса.

Обработка событий, связанных с элементом


TextField
П р и работе с элементом T e x t F i e l d для разработчика представляют интерес че­
тыре типа событий: события, связанные с фокусом ввода, с клавиат\рой, текстовые
события и событие A c t i o n E v e n t . События, связанные с фокусом ввода, генерируют­
ся в тот момент, когда элемент получает или теряет фокус. События, связанные с кла­
виатурой, и текстовые события возникают, когда пользователь нажимает клавишу
клавиатуры, при условии что поле редактирования имеет фокус ввода. Событие
A c t i o n E v e n t генерируется, когда пользователь нажимает клавишу <Enter> при нали­
чии у элемента фокуса ввода. Различие между событиями клавиатуры и текстовыми
событиями состоит в том, что последние генерируются даже при вызове метода
s e t T e x t из программы; события клавиатуры в этом случае отсутствуют. Как обычно,
вы имеете возможность организовать низкоуровневую обработку событий самим
компонентом T e x t F i e l d , переопределив метод p r o c e s s X x x E v e n t . Кроме того, с
объектом можно связать внешний обработчик. П р и м е р низкоуровневой обработки
событий приведен в разделе 11.6.

13.20. Класс TextArea


Класс T e x t A r e a похож на класс T e x t F i e l d , за исключением того, что объект
данного класса поддерживает работу с несколькими строками текста и не генерирует
события A c t i o n E v e n t . П р и м е р кода, создающего текстовую область, приведен ниже.
T e x t A r e a i n p u t A r e a = new T e x t A r e a ( 4 , 1 5 ) ;
add(inputArea);
T e x t A r e a r e s u l t s A r e a = new T e x t A r e a ( " N o R e s u l t s Y e t " , 2, 10);
resultsArea.setEditable(false) ;
add(resultsArea);

Конструкторы
Разработчику, использующему T e x t A r e a , доступны пять конструкторов данного
класса.

public TextAreaO
Этот конструктор создает пустую текстовую область, содержащую вертикальную и
горизонтальную полосы прокрутки. Количество строк и столбцов зависит от one-
524 Глава 13. Компоненты AWT

р а ц и о н н о й системы. Заметьте, что размеры создаваемой области могут быть вели­


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

public TextArea(int rows, int cols)


Данный конструктор создает пустую текстовую область с заданным количеством
строк и столбцов. Ш и р и н а элемента вычисляется на базе усредненного размера
символов, отображаемых конкретным шрифтом. Создаваемый объект T e x t A r e a
включает вертикальную и горизонтальную полосы прокрутки.

p u b l i c TextArea(String initialString)
Данный конструктор создает текстовую область, которая содержит вертикальную
и горизонтальную полосы прокрутки и инициализирует элемент указанной стро­
кой текста. Строка, передаваемая в качестве параметра, может содержать символы
перевода строки ( \ п ) .

public TextArea(String initialString, int rows, int cols)


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

public TextArea(String initialString, int rows, int c o l s , int scrollbar Type)


Данный конструктор создает текстовую область указанного размера и инициализи­
рует ее строкой. Если значение параметра s c r o l l b a r T y p e равно T e x t A r e a .
SCROLLBARS BOTH, данный констр)тстор выполняет те же дер1ствия, что и предыду­
щий. Если задано значение T e x t A r e a . SCROLLBARS_VERTICAL_ONLY, текстовая об­
ласть содержит только вертикальную полосу прокрутки. Если длина строки больше
ширины окна, часть текста переносится на следующую строку. Значение T e x t A r e a .
SCROLLBARS_HORIZONTAL_ONLY приводит к созданию только горизонтальное! по­
лосы прокрутки. l i , наконец, значение T e x t A r e a . SCROLLBARS_NEITHER указывает
на то, что текстовая область не должна содержать полосы прокрутки. При необхо­
димости текст переносится на следующую строку.

Примеры текстовых областей


в листинге 13.37 показан аплет, в котором создаются две текстовые области: одна
из них пустая, а в другой отображаются три строки. Результаты выполнения аплета в
среде Windows 98 показаны на рис. 13.22.

Листинг 1 3 . 3 7 . T e x t A r e a s . J a v a

import Java.applet.Applet;
import java.awt.*;

p u b l i c c l a s s TextAreas extends Applet


public void i n i t ( ) {
setBackground(Color.lightGray);
13.20. Класс TextArea 525

add(new TextArea(3, 10));


add(new TextArea("Some\nInitial\nText", 3, 10)

iiiiiiiifc'.'.'''jM^f
Apptel

1 J Some
Initial
Text

J i
J
Рис. 13.22, Текстовые области, отображаемые
Applet started
в Windows 98

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


От своего суперкласса ( T e x t C o m p o n e n t ) текстовая область наследует g e t T e x t ,
s e t T e x t , s e t E d i t a b l e , s e l e c t и другие методы. Кроме того, класс T e x t A r e a под­
держивает методы, описанные ниже.

public void a p p e n d ( S t r i n g additionalText)


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

public int getColumns()


Метод g e t C o l u m n s возвращает число столбцов в текстовой области.

public int getRows()


Метод g e t R o w s возвращает число строк в текстовой области.

public void insert(String additionalText, int i n d e x )


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

public void r e p l a c e R a n g e ( S t r i n g r e p l a c e m e n t , int s t a r t l n d e x , int e n d l n d e x )


Метод r e p l a c e R a n g e заменяет заданной строкой текст, начинающийся в позиции
s t a r t l n d e x и заканчивающийся в позиции e n d l n d e x . Длина строки и текста, ко­
торый она замещает, может различаться.
Поскольку текстовая область является компонентом, объекту T e x t A r e a доступны
методы класса Component.
526 Глава 13. Компоненты AWT

Поддержка событий TextArea


п р и работе с текстовой областью текстовые события и события, связанные с кла­
виатурой и фокусом ввода, обрабатываются так же, как и поля редактирования. Объ­
ект T e x t A r e a не генерирует события A c t i o n E v e n t .

1 3 . 2 1 . Класс Label
Объект L a b e l предназначен для отображения статического текста. В большинст­
ве случаев использовать L a b e l удобнее, чем выводить текст с помощью метода
d r a w s t r i n g объекта G r a p h i c s , поскольку строку, созданную с помощью L a b e l , дис­
петчер компоновки автоматически размещает в контейнере. Требуемый текст указы­
вается при создании объекта L a b e l , после чего этот объект помещается в окно.
Label label = new Label("...");
add(label);
Часто текст, отображаемый посредством L a b e l , предназначается для описания
другого объекта и должен выравниваться в соответствии с ним. Диспетчер компонов­
ки F l o w L a y o u t мало пригоден для обеспечения такого поведения, поэтому прихо­
дится использовать другие диспетчеры. П р и работе с объектами L a b e l можно изме­
нять ш р и ф т и тип выравнивания. Например, в следующием фрагменте кода L a b e l
используется в качестве заголовка панели.
P a n e l r e s u l t s P a n e l = new P a n e l ( ) ;
resultsPanel.setLayout(new BorderLayout());
L a b e l t i t l e = new L a b e l ( " R e s u l t s " , Label.CENTER);
t i t l e . s e t F o n t ( n e w F o n t ( " S a n s S e r i f " , Font.BOLD, 1 8 ) ) ;
r e s u l t s P a n e l . a d d ( t i t l e , BorderLayout.NORTH);
T e x t A r e a r e s u l t s A r e a = new T e x t A r e a ( ) ;
r e s u l t s P a n e l . a d d ( r e s u l t s A r e a , BorderLayout.CENTER);

Конструкторы
в классе L a b e l предусмотрены три конструктора.

public Label()
public Label(String labelString)
public Label(String labelString, int alignment)
Первый конструктор создает пустой объект L a b e l . Текст к такому объекту можно
добавить позже, используя для этого метод s e t T e x t . Второй конструктор создает
объект, содержащий указанную строку. Последний вариант конструктора задает так­
же тип выравнивания. Параметр a l i g n m e n t может принимать значения L a b e l . LEFT
(по умолчанию), L a b e l . RIGHT и L a b e l .CENTER. Если объект L a b e l содержится в
окне, с которым связан диспетчер компоновки F l o w L a y o u t , либо включен в область
E a s t или West B o r d e r L a y o u t , то тип выравнивания не имеет практически никакого
1 3 . 2 1 . Класс Label 527

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


щейся в нем строки. Если же размеры L a b e l приходится изменять вручную либо если
данный объект находится в области NORTH или SOUTH диспетчера B o r d e r L a y o u t , а
также если он включен в ячейку G r i d L a y o u t или G r i d B a g L a y o u t , то размеры ком­
понента могут быть намного больше размеров строки. В этом случае необходимо за­
давать тип выравнивания, чтобы правильно разместить текст в области.

Пример использования объектов Label


в листинге 13.38 создаются четыре объекта Label, для которых заданы разные
шрифты и типы выравнивания. Результаты выполнения кода приведены на рис. 13.23.

Листинг 13.38.Labels.Java

import Java.applet.Applet;
import java.awt.*;

public class Labels extends Applet {


public void initO {
setLayout(new GridLayout(4,1));
Label labell, label2, labelS, label4;
labell = new Label("Label 1");
label2 = new Label("Label 2", Label.LEFT);
label3 = new Label("Label 3", Label.RIGHT);
label4 = new Label("Label 4", Label.CENTER);
Font bigFont -= new Font ("Sanserif", Font.BOLD, 25);
label2.setFont(bigFont);
labels.setFont(bigFont);
label4.setFont(bigFont);
add(labell)
add(label2)
adddabelS)
add(label4) ,

r^LWHIiMMi'." ЖЭТР-Тй^
Applet
Label

Label 2
Label 3
Label 4 | Рис. 13.23. Объекты Label могут отображать текст
Applet started
различными шрифтами с различными типами выравнивания

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


в классе L a b e l определены семь методов. Ч е т ы р е из них, которые используются
наиболее часто, описаны ниже.
528 Глава 13. Компоненты AWT

public int getAlignment()


public v o i d setAlignment(int alignment)
Первый из указанных методов возвращает текущий тип выравнивания. Этот метод
может возвращать значение L a b e l . LEFT, L a b e l .RIGHT или L a b e l .CENTER. Вто­
рой метод задает тип выравнивания.

public String getText()


public void setText(String newLabel)
Метод g e t T e x t возвращает текст, содержащийся в объекте L a b e l . В случае пусто­
го элемента L a b e l возвращается строка нулевой длины (но не объект n u l l ) . Ме­
тод s e t T e x t изменяет текст, содержащийся в объекте L a b e l . Если данный эле­
мент уже отображается, замена текста не приведет к изменению его размеров.
Чтобы это произошло, для включающего окна должны быть вызваны методы
invalidate и validate.
someLabel.setText("А Different Label");
someLabel.getParent{).invalidate();
someLabel.getParent().validate();
Как подкласс C o m p o n e n t , L a b e l наследует средства установки цвета, шрифта и
изменения размеров, а также другие возможности данного класса.

Поддержка событий Label


Объекты L a b e l получают события, связанные с мышью и клавиатурой, однако со­
бытия выбора и событие A c t i o n E v e n t не генерируются. В листинге 13.39 показан
код приложения, в котором в окно включаются два элемента R e v e r s i b l e L a b e l
(листинг 13.40). К объекту R e v e r s i b l e L a b e l подключен обработчик событий мыши;
когда к)^сор мыши попадает в область, занимаемую элементом, изменяются цвет фо­
на и цвет переднего плана элемента. Когда курсор мыши покидает пределы области,
восстанавливаются предыдущие цвета. На рис. 13.24 показан внешний вид окна после
запуска приложения, а на рис. 13.25 — окно после указания курсором мыши на один из
элементов R e v e r s i b l e L a b e l .

Листинг 1 3 , 3 9 . R e v e r s e L a b e l s . J a v a

import java.awt.*;

public class ReverseLabels extends ClcseableFrame {


public static void main(String[] args) {
new ReverseLabels();

public ReverseLabels () {
super("Reversible Labels");
setLayout(new FlowLayout());
setBackground(Color.lightGray);
setFont(new Font("Serif", Font.BOLD, 18))
ReversibleLabel labell =
13.21. Класс Label 529

new ReversibleLabel("Black on White",


Color.white, Color.black);
add(labell);
ReversibleLabel label2 =
new ReversibleLabel("White on Black",
Color.black, Color.white);
add(label2);
packO ;
setVisible(true);

Листинг 13.40.ReversibleLabel.Java

import java.awt.*;
import Java.awt.event.*;
/** Объект Label, который при помещении на него курсора
* мыши изменяет цвет фона и цвет переднего плана.
Ч
public class ReversibleLabel extends Label {
public ReversibleLabel(String text,
Color bgColor, Color fgColor) {
super(text);
MouseAdapter reverser = new MouseAdapter() {
public void mouseEntered(MouseEvent event) {
reverseColors();
}

public void mouseExited(MouseEvent event) {


reverseColors0; // либо mouseEntered(event);

addMouseListener(reverser);
setText(text);
setBackground(bgColor);
setForeground(fgColor);
}

protected void reverseColors()


Color fg = getForeground();
Color bg = getBackground();
setForeground(bg);
setBackground(fg);
}
530 Глава 13. Компоненты AWT

Wliite on Black Рис. 13.24. Внешний вид окна R e v e r s i b l e L a b e l s после


запуска программы

Рис. 13.25. Так выглядит окно R e v e r s i b l e L a b e l s после


указания мышью на элемент, расположенный справа

13.22. Полосы прокрутки и линейные


регуляторы
В AWT для реализации полос прокрутки и линейных регуляторов используется
один и тот же класс S c r o l l b a r . П о н я т и я "нормального размера" для полос прокрут­
ки не существует и эти элементы не используются в окнах, связанных с диспетчером
компоновки F l o w L a y o u t . О б ы ч н о полосы прокрутки помещаются в области E a s t и
S o u t h диспетчера B o r d e r L a y o u t либо используются при работе с G r i d L a y o u t .
Кроме того, размеры элемента S c r o l l b a r можно устанавливать вручную.
К сожалению, полосы прокрутки реализуются в различных Java-системах по-
разному и непосредственное их использование затруднено. Для прокрутки содержи­
мого окон часто применяют элементы T e x t A r e а и S c r o l l Р а п е . Если вы разрабаты­
ваете аплет или приложение на базе Swing, можете использовать для реализации ли­
нейных регуляторов класс J S l i d e r .

Конструкторы
в классе S c r o l l b a r предусмотрены следующие конструкторы.

public ScroUbarO
Данный конструктор создает вертикальную полосу прокрутки. Размер ползунка
(перемещаемого элемента) по умолчанию составляет 10% от общей длины области
перемещения. Внутренние минимальное и максимальное значения (они будут
описаны ниже) устанавливаются равными нулю.

public Scrollbar(int orientation)


Указанный конструктор создает горизонтальную или вертикальную полосу про­
крутки. Параметр o r i e n t a t i o n может принимать значение Scrollbar.
HORIZONTAL или S c r o l l b a r .VERTICAL. Размеры ползунка по умолчанию состав­
ляют 10% от общей длины области перемещения. Внутренние минимальное и мак­
симальное значения (они будут описаны ниже) устанавливаются равными нулю.
1 3 . 2 2 . Полосы прокрутки и линейные регуляторы 531

public Scrollbar(int orientation, int initialValue, int bubbleSize, int min, int max)
Данный конструктор используется для создания линейного регулятора. Он создает
горизонтальный или вертикальный регулятор либо полосу прокрутки; при этом
задаются размер ползунка и диапазон значений. Размер ползунка указывается от­
носительно диапазона значений. Так, если разность между величинами max и min
равна 5, то размер ползунка будет равен 20% от размера области перемещения.
Заметьте, что ряд операционных систем (например, некоторые версии MacOS) не
поддерживает изменение размеров ползунка.
Конкретное значение соответствует расположению не центра ползунка, а его ле­
вого (для горизонтального регулятора) либо верхнего (для вертикального регуля­
тора) края. Для горизонтальной полосы прокрутки максимальное значение соот­
ветствует не крайнему правому положению левой части ползунка, а крайнему пра­
вому положению его правой части. Это приводит к тому, что реальный диапазон
значений оказывается меньше разности между максимальным и минимальным
значениями. Например, приведенное ниже выражение создает полосу прокрутки,
значения которой изменяются в диапазоне от О до 45; начальное значение равно
25, а размер ползунка — 5.
new S c r o l l b a r ( S c r o l l b a r . H O R I Z O N T A L , 25, 5, О, 50);

Внимание!

При создании полосы прокрутки с размером ползунка t помните,


для того чтобы значения элемента изменялись в диапазоне от т±п
до max, надо установить максимальное значение, равное шах ч- t .

Примеры линейных регуляторов


На рис. 13.41 показан аплет, в котором создаются различные вертикальные и го­
ризонтальные л и н е й н ы е регуляторы (элементы S c r o l l b a r , предназначенные для
выбора значений). Для всех регуляторов задан диапазон от О до 100 и начальное зна­
чение, равное 50. Регуляторы отличаются один от другого размерами ползунков. Ре­
зультаты выполнения аплета в системе Windows 98 показаны на рис. 13.26.

Листинг 1 3 . 4 1 . S c r o l l b a r s . J a v a

import Java.applet.Applet;
import java.awt.*;

public c l a s s S c r o l l b a r s extends Applet {


public void i n i t O {
int i;
setLayout(new G r i d L a y o u t ( 1 , 2)) ;
P a n e l l e f t = new P a n e l ( ) , r i g h t = new P a n e l ( ) ;
left.setLayout(new GridLayout(10, 1 ) ) ;
f o r ( i = 5 ; i < 5 5 ; i=i-b5) {
l e f t . a d d ( n e w Scrollbar(Scrollbar.HORIZONTAL, 50, i , 0, 100));
}
532 Глава 13. Компоненты AWT

right.setLayout(new GridLayout(1, 10));


for (1=^5; i<55; 1=1 + 5) {
rlght.add(new Scrollbar(Scrollbar.VERTICAL, 50, 1, 0, 100)
}
add(left);
add(right);

1Ч!11ПИ1!,1-!1И^
J. dL±i

Рис. 13.26. Элементы S c r o l l b a r


с одинаковыми диапазонами и начальными
H'^NKh значениями, но с различными размерами
ползунков

Методы Scrollbar
Разработчику, использующему класс S c r o l l b a r , доступны следующие методы.

public v o i d a d d A d j u s t m e n t L i s t e n e r (AdjustmentListener listener)


public void r e m o v e A d j u s t m e n t L i s t e n e r ( A d j u s t m e n t L i s t e n e r listener)
Метод a d d A d j u s t m e n t L i s t e n e r связывает с объектом S c r o l l b a r обработчик
A d j u s t m e n t L i s t e n e r , который позволяет отслеживать действия пользователя с
объектом. В обработчике A d j u s t m e n t L i s t e n e r должен быть реализован метод
a d j u s t m e n t V a l u e C h a n g e d , которому в качестве параметра передается объект
A d j u s t m e n t E v e n t . Класс A d j u s t m e n t E v e n t содержит метод g e t A d j u s t m e n t -
Туре, который возвращает одно из следующих значений: A d j u s t m e n t E v e n t . -
UN1T_INCREMENT (пользователь щелкнул мышкой на стрелке, направленной впра­
во или вниз), A d j u s t m e n t E v e n t .UNIT_DECREMENT (щелчок на стрелке, направ­
ленной влево или вверх), A d j u s t m e n t E v e n t . BLOCK_INCREMENT (щелчок в облас­
ти перемещения справа или снизу от ползунка), A d j u s t m e n t E v e n t . BLOCK_DEC-
REMENT (щелчок в области перемещения слева или сверху от ползунка) и
A d j u s t m e n t E v e n t . TRACK (перетаскивание ползунка мышью).

public int g e t U n i t l n c r e m e n t O
public void s e t U n i t I n c r e m e n t ( i n t i n c r e m e n t )
Метод g e t U n i t I n c r e m e n t предназначен для определения того, насколько изме­
нилось значение в результате щелчка на одной из стрелок, расположенных на
концах области перемещения. Если разработчик не предусмотрел специальных
действий в методе s e t U n i t l n c r e m e n t , возвращается величина, равная 1. Заметь­
те, что в некоторых реализациях класса, например в JDK 1.1 в системе Solans, дан­
ный метод игнорируется. Разрабатывая приложение, необходимо предусмотреть
13.22. Полосы прокрутки и линейные регуляторы 533

такое поведение класса и принять соответствующие меры. Метод s e t U n i t I n c r e ­


m e n t изменяет перемещение ползунка полосы прокрутки после щелчка на стрел­
ках. Данный метод поддерживается не на всех платформах.

public int getMaximum()


public void setMaximum(int maxValue)
Метод getMaximum возвращает максимальное значение для объекта S c r o l l b a r .
Это значение превышает наибольшее значение, которое может быть установлено
перемещением ползунка, на величину, возвращаемую методом g e t V i s i b l e -
Amount. Метод setMaximum позволяет изменить максимальное значение.

public int g e t M i n i m u m ( )
public v o i d s e t M i n i m u m ( i n t minValue)
Метод g e t M i n i m u m возвращает, a метод s e t M i n i m u m устанавливает минимальное
значение для объекта S c r o l l b a r .

public int getOrientation()


public void setOrientation(int orientation)
Данные методы позволяют определить и задать ориентацию объекта S c r o l l b a r .
Для указания ориентации используются константы S c r o l l b a r . HORIZONTAL и
Scrollbar.VERTICAL.

public int g e t B l o c k I n c r e m e n t ( )
public void s e t B l o c k I n c r e m e n t ( i n t i n c r e m e n t )
Метод g e t B l o c k l n c r e m e n t возвращает, a метод s e t B l o c k l n c r e m e n t устанавли­
вает изменение значения после щелчка мышью в области перемещения за преде­
лами ползунка. Изменение значения по умолчанию зависит от платформы. В сис­
теме Windows 98 оно равно 10, а в системе Solaris — размеру ползунка.

public int getValueO


public void setValue(int value)
public void setValues(int value, int bubbleSize, int m i n , int max)
Данные методы позволяют определить и установить текущее значение полосы
прокрутки. П р и указании значения меньше минимального ошибка не возникает,
вместо этого устанавливается минимальное значение. Значение, превышающее
наибольшее из возможных, также не приводит к ошибке, вместо этого устанавли­
вается максимальное значение минус размер ползунка. Последний метод предна­
значен для одновременного изменения нескольких параметров.

public int getVisibleAmount()


Данный метод возвращает размер ползунка. Размер определяется не в пикселях, а ^
в единицах диапазона значений.
534 Глава 13. Компоненты AWT

public void processAdjustmentEvent(AdjustmentEvent event)


Если вы хотите, чтобы объект S c r o l l b a r самостоятельно выполнял низкоуров­
невую обработку своих событий, необходимо сначала разрешить передачу собы­
тий с помощью следующей команды:
enableEvents(AWTEvent.ADJUSTMENT_EVENT_MASK);
Затем надо переопределить метод p r o c e s s A d j u s t m e n t E v e n t , которому в качест­
ве параметра передается объект A d j u s t m e n t E v e n t . Если с объектом связываются
обработчики, необходимо в переопределенный метод включит вызов s u p e r .
p r o c e s s A d j u s t m e n t E v e n t . П р и м е р обработки событий приведен ниже.
Поскольку S c r o l l b a r является подклассом класса Component, ему доступны ме­
тоды s e t F o r e g r o u n d и s e t B a c k g r o u n d , однако метод s e t F o r e g r o u n d не всегда
поддерживается. В Windows 9 5 / N T метод s e t B a c k g r o u n d устанавливает цвет облас­
ти перемещения. В Solaris этот метод задает цвет ползунка и стрелок. Во многих реа­
лизациях MacOS цвета в объекте S c r o l l b a r не поддерживаются.

Поддержка событий Scrollbar


Для обработки событий S c r o l l b a r надо либо переопределить метод p r o c e s s ­
A d j u s t m e n t E v e n t , либо связать с объектом обработчик A d j u s t m e n t L i s t e n e r . Если
вы переопределите p r o c e s s A d j u s t m e n t E v e n t , обрабатывать события будет сам
объект S c r o l l b a r . П р и использовании A d j u s t m e n t L i s t e n e r можно поручить об­
работку событий внешнему объекту. В этом случае вам придется использовать два ме­
тода класса A d j u s t m e n t E v e n t : g e t V a l u e и g e t A d j u s t m e n t T y p e . Метод g e t V a l u e
возвращает целое число, соответствующее текущему значению S c r o l l b a r . Метод
g e t A d j u s t m e n t T y p e возвращает одно из следующих значений: A d j u s t m e n t E v e n t .
UNIT_INCREMENT, AdjustmentEvent.UNIT_DECREMENT, A d j u s t m e n t E v e n t . B L O C K _
INCREMENT, AdjustmentEvent.BLOCK_DECREMENT или A d j u s t m e n t E v e n t . T R A C K .
В листинге 13.42 демонстрируется использование переопределенного метода
p r o c e s s A d j u s t m e n t E v e n t для обеспечения работы полосы прокрутки, которая
поддерживает единичные и блоковые приращения. П р и этом элемент работает неза­
висимо от объекта, использующего данные значения. Обратите внимание на то, что
данный подход дает результаты только в том случае, когда состояние полосы про­
крутки изменяется в результате действий пользователя. Вызов s e t V a l u e из програм­
мы не приведет к изменению значения переменной l a s t V a l u e и обойти это ограни­
чение невозможно, поскольку нет способа различить системный вызов s e t V a l u e и
вызов этого метода по инициативе пользователя.
Заметьте, что Swing API предоставляет класс J S l i d e r (он будет описан в следую­
щей главе), который значительно лучше реализует все функции S c r o l l b a r и работа­
ет одинаково на всех платформах. Однако компоненты Swing поддерживаются не
всеми броузерами и для того, чтобы аплет, использующий эти компоненты, работал
корректно, необходимо установить Java Plug-In.

Листинг 1 3 . 4 2 . B e t t e r S c r o l l b a r . Java

import java.awt.*;
import Java.awt.event.*;
13.23. Контекстные меню 535

/•• Модифицированная полоса прокрутки. При ее создании


* учитывался тот факт, что часто методы приращения
* строк и страниц игнорируются. Данный класс
* демонстрирует низкоуровневую обработку событий.
Ч
public class BetterScrollbar extends Scrollbar {
private int lastValue;

public BetterScrollbar(int orientation,


int initialValue,
int bubbleSize,
int min,
int max) {
super(orientation, initialValue, bubbleSize, min, max);
enablGEvents (AWTEvent .ADJUSTMENT_EVENT_MASK) ;
lastValue = initialValue;

/** Обработка событий, связанных с приращениями значений.


V
public void processAdjustmentEvent(AdjustmentEvent e) {
int type = e.getAdjustmentType();
switch(type) {
case AdjustmentEvent.UNIT_INCREMENT:
setValue(lastValue + getUnitlncrement());
break;
case Adj ustmentEvent.UNIT_DECREMENT:
setValue(lastValue - getUnitlncrement());
break;
case Adj ustmentEvent.BLOCK_INCREMENT:
setValue(lastValue + getBlocklncrement());
break;
case Adj ustmentEvent.BLOCK_DECREMENT:
setValue(lastValue - getBlocklncrement());
break;
}
lastValue = getValueO;
super.processAdjustmentEvent (e);
}

П о адресу http://www.corewebprograiraning.com/ вы найдете реализацию


класса Slider, которая объединяет в одном элементе графического пользовательско­
го интерфейса компоненты Scrollbar и TextField.

13.23. Контекстные меню


Контекстные, или раскрывающиеся меню очень просты в использовании. Для то­
го чтобы создать раскрывающееся меню, надо создать объект PopupMenu, включить в
536 Глава 13. Компоненты AWT

него объекты Menu I t em, а затем проверить флаг раскрывающегося меню компонен­
та, вызвав метод i s P o p u p T r i g g e r в обработчике события мыши. П р и получении
флага надо вызвать объект show для отображения меню. Когда раскрывающееся ме­
ню отображается на экране, для пунктов меню генерируются события A c t i o n E v e n t .

Конструкторы
в классе PopupMenu определены следующие конструкторы.

public PopupMenu()
public P o p u p M e n u ( S t r i n g title)
Первый из указанных конструкторов создает раскрывающееся меню без заголовка,
второй добавляет к создаваемому меню указанный заголовок, который, однако, не
отображается в системах Windows 9 5 / 9 8 / N T .

Пример использования раскрывающегося меню


в листинге 13.43 показан код аплета, при выполнении которого создается раскры­
вающееся меню. Обработчик событий м ы ш и проверяет флаг раскрывающегося меню
и при его наличии отображает меню на экране. Заметьте, что аплет реализует интер­
фейс ActionListener и выполняет функции обработчика событий для каждого
пункта меню. При выборе пункта вызывается метод actionPerformed. Результаты
выполнения аплета в системе Windows 95 показаны на рис. 13.27.

Листинг 13.43.СоlorPopupMenu.java

import Java . applet .Applets-


import java.awt.*;
import Java.awt.event.*;

/** Пример использования раскрывающихся меню. */

public class ColorPopupMenu extends Applet


implements ActionListener {
private String[] colorNames =
{ "White", "Light Gray", "Gray", "Dark Gray", "Black" };
private Color[] colors =
{ Color.white. Color.lightGray, Color.gray.
Color.darkGray, Color.black };
private PopupMenu menu;

/** Создание объекта PopupMenu и включение в него Menultem. */

public void initO {


setBackground(Color.gray);
menu = new PopupMenu("Background Color");
enableEvents (AWTEvent.MOUSE_EVENT_MASK) ;
Menultem colorName;
for(int i=0; i<colorNames.length; i++) {
colorName = new Menultem(colorNames[i]);
13.23. Контекстные меню 537

menu.add(colorName);
colorName.addActionListener(this);
menu.addSeparator() ;
}
add(menu) ;

/•• MouseListener не используется, поскольку для Win95/98/NT


* необходимо проверять isPopupTrigger в mouseReleased,
* а для Solaris — в mousePressed.
^/
public void processMouseEvent(MouseEvent event) {
if (event.isPopupTrigger0) {
menu.show(event.getComponent(), event.getX(),
event.getY());
}
super.processMouseEvent(event);
}

public void actionPerformed(ActionEvent event) {


setBackground(colorNamed(event.getActionCommand()))
repaint();
}

private Color colorNamed(String colorName) {


for(int i=0;i<colorNames.length; i++) {
if(colorNames[i].equals(colorName)) {
return(colors[i]);
}
}
return(Color.white);

[^Applet 4ie¥ntrtJBk^api»fMemJ^ti^ iPPPCfDfxr


Applet

^''V^f'''''', '''-;V'',','-''; '^*''' ^'''''^


£гй"^Ш^'ШЩ
p;' l''"' 'X '':'//, T'- ^^^^

Щ^:[:!''!'У''':'''-'-' :<" ''/'"/.''-


Ш' i'Zc-''^'<,?-г--'.1Г/!гЧ-'•'-- ^^^''-Wv'r:'''X'?^У<''Я Рис. 13.27. В системе Windows 98 заголовок
Applet started
раскрывающегося меню не отображается
538 Глава 13. Компоненты AWT

Методы класса РорирМепи


Непосредственно в классе PopupMenu определен один метод show, который ото­
бражает меню. Этот метод, а также методы, унаследованные от суперкласса Menu, и
методы Menu I t em описаны ниже.

public void a d d ( M e n u I t e m item)


public void add(String label)
Данные методы предназначены для добавления пунктов меню. Вместо того чтобы
непосредственно включать строку в меню, ее лучше о ф о р м и т ь в виде объекта
M e n u l t e m , используя выражение M e n u l t e m ( l a b e l ) . Это позволит вам связать с
пунктом меню обработчик A c t i o n L i s t e n e r .

public v o i d addActionListener(ActionListener listener)


Данный метод позволяет подключать к объекту M e n u l t e m обработчик события,
связанного с выбором пункта меню. П р и обработке события текст пункта меню
можно получить с помощью метода g e t A c t i o n C o m m a n d ( ) .

public void addSeparatorO


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

public void setShortcut(MenuShortcut shortcut)


public M e n u S h o r t c u t getShortcut()
public void deleteShortcut()
JDK 1.1 и более поздние версии предоставляют возможность связывать с каждым
пунктом меню комбинацию клавиш. Для этого надо сначала создать объект
M e n u S h o r t c u t посредством следующего выражения:
MenuShortcut shortcut = new M e n u S h o r t c u t ( i n t key);
Затем необходимо добавить этот объект к M e n u l t e m с помощью метода
s e t S h o r t c u t . Метод g e t S h o r t c u t позволяет получить объект M e n u S h o r t c u t , а
метод d e l e t e S h o r t c u t удаляет соответствие между комбинацией клавиш и пунк­
том меню.

public void s h o w ( C o m p o n e n t с, int х, int у)


Данный метод отображает раскрывающееся меню в указанной позиции относи­
тельно верхнего левого угла компонента.
Объект PopupMenu является компонентом, поэтому он позволяет выбирать
ш р и ф т ы и цвет.
13.23. Контекстные меню 539

Поддержка событий, связанных


с раскрывающимися меню
При работе с раскрывающимися меню надо обрабатывать два типа событий. Пер­
вое событие — это щелчок мышью, в ответ на которое меню отображается на экране.
Второе событие связано с выбором пункта меню.
Чтобы избавить разработчика от необходимости помнить, какое действие с мы­
шью должно вызывать отображение раскрывающегося меню, Java предоставляет для
этой цели удобный метод i s P o p u p T r i g g e r класса M o u s e E v e n t . Поскольку одни опе­
рационные системы (например, Solaris) вызывают раскрывающееся меню после на­
жатия кнопки мыши, а другие (например, Windows 98 и Windows NT) — после отпус­
кания кнопки, не стоит проверять флаг раскрывающегося меню в объекте Mouse~
L i s t e n e r , лучше сделать это в теле метода p r o c e s s M o u s e E v e n t . Если вы все же ре­
шили использовать M o u s e L i s t e n e r , то проверку надо включить и в метод
m o u s e P r e s s e d , и в метод m o u s e R e l e a s e d . Процедура вызова меню выглядит при­
близительно следующим образом.
PopupMenu menu = new P o p u p M e n u ( " [ T i t l e ] " ) ;

e n a b l e E v e n t s (AWTEvent .MOUSE__EVENT__MASK) ;

public void processMouseEvent(MouseEvent event) {


if (event.isPopupTrigger())
menu.show(event.getComponent(),
event.getX(), event.getY());
super.processMouseEvent(event);
}

Для обработки событий, связанных с выбором пункта меню, следует связать с каж­
дым из объектов M e n u l t e m обработчик A c t i o n L i s t e n e r .
Menultem i t e m = new M e n u l t e m ( " [ L a b e l ] " ) ;
menu.add(item);
item.addActionListener(someListener);
При использовании обработчика A c t i o n L i s t e n e r необходимо реализовать ме­
тод a c t i o n P e r f o r m e d , которому в качестве параметра передается объект
A c t i o n E v e n t . Метод g e t A c t i o n C o m m a n d класса A c t i o n E v e n t позволяет получить
текст, соответствующий конкретному объекту M e n u l t e m .

13.24. Резюме
AWT предоставляет восемь типов окон, которые разработчик может использовать
при создании пользовательского интерфейса: C a n v a s , P a n e l , A p p l e t , S c r o l l P a n e ,
Frame, D i a l o g , F i l e D i a l o g и Window. В данной главе описаны различия между этими
окнами и рассмотрены основные вопросы, связанные с их использованием. Разработ­
чику программ на Java доступны следующие типы управляющих элементов: B u t t o n ,
Checkbox (с помощью этого элемента создаются флажки опций и переключатели оп­
ций). C h o i c e , L i s t , T e x t F i e l d , T e x t A r e a , L a b e l , S c r o l l b a r (используется для соз­
дания полос прокрутки и линейных регуляторов) и PopupMenu. Для того чтобы исполь-
540 Глава 13. Компоненты AWT

зовать компоненты пользовательского интерфейса, вы должны, во-первых, знать, как


создать элемент и придать ему требуемый внешний вид, а во-вторых, представлять себе
процесс обработки событий, генерируемых в ответ на действия пользователя с этим
элементом. В большинстве случаев основные характеристики, определяюш^ие внешний
вид элемента, задаются в констр)тсторе класса, но некоторые компоненты ( C h o i c e ,
L i s t , PopupMenu) создаются "пустыми", а затем заполняются. Наиболее важным типом
событий, генерируемых при работе с интерфейсными элементами, являются, наверное.
A c t i o n E v e n t , однако события, связанные с выбором пунктов меню, действиями с кла­
виатурой и мышью, прокруткой, и текстовые события также важны.
Прочитав данную главу, вы познакомились с компонентами, предоставляемыми
AWT для разработки графического пользовательского интерфейса. П р и разработке
аплетов вы, вероятнее всего, ограничитесь этими компонентами, поскольк}^ более
новые средства Swing, поддерживаемые платформой Java 2, больше подходят для не­
зависимых приложений с графическим интерфейсом. Преимущество AWT состоит в
том, что аплеты, созданные на его основе, могут выполняться в большинстве версий
Netscape и Internet Explorer. Java 1.1 AWT API поддерживается Netscape 4.06, Internet
Explorer 4.0 и более поздними версиями этих броузеров.
о с н о в ы SWING

В ЭТОЙ главе...

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

Выбор стиля пользовательского интерфейса.

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

Создание кнопок с текстом и изображениями..

Использование HTML в кнопках и статическом тексте.

Выбор цвета с помощью JCoiorChooser.

Отображение сообщений.

Создание дочерних окон в приложениях.

Создание панелей инструментов.

Реализация Web-броузера в Swing.


J~y\zJSJ:EJ

ногие разработчики считают появление Swing наиболее важным дополнени­

М ем платформы Java. В пакете Swing был реализован ряд средств (пиктограммы


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

• Расширенный набор встроенных управляющих элементов, включая кнопки с


изображениями, панели со вкладками, линейные регуляторы, средства выбора
цвета панели инструментов, текстовые области с отображением HTML- и RTF-
данных, списки, деревья и таблицы.
• Возможность настройки различных характеристик компонентов, включая сти­
ли обрамления, выравнивание текста и средства рисования. Практически с ка­
ждым управляющим элементом может быть связано изображение.
• Возможность изменения стиля интерфейса в процессе выполнения програм­
мы. П р и необходимости вы можете создать собственный тип интерфейса.
• Многочисленные новые средства, например встроенная поддержка двойной
буферизации, подсказки, плавающие панели инструментов, поддержка комби­
наций клавиш и управляющие элементы, определяемые пользователем.
Подробное рассмотрение библиотеки Swing не входит в задачу данной книги. Мы
обсудим лишь средства, наиболее часто используемые при построении приложений.
Дополнительную информацию о компонентах Swing и их применении вы найдете в
книгах Кима Топли (Kim Topley) Core Java Foundation Classes и Core Swing: Advanced Pro-
camming.
544 Глава 14. Основы Swing

1 4 . 1 . Общие сведения о Swing


Средства Swing предусмотрены спецификацией Java 2, но были включены B J D K 1.1
как независимый пакет. Версия Swing JDK 1.1 доступна по адресу h t t p : / / j a v a .
s u n . c o m / p r o d u c t s / j f c / d o w n l o a d . h t m l . Компоненты Swing можно использовать в
аплетах. Однако следует помнить, что из всех популярных броузеров их поддержива­
ет только Netscape 6. П р и использовании других броузеров для работы со средствами
Swing необходимо устанавливать Java Plug-In (продукт Java Plug-In был рассмотрен в
главе 9).

Различия между Swing и А WT


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

Соглашения об именовании
Имена компонентов Swing начинаются с символа "J" и представляются в формате
J Ххх, где Ххх— обозначение компонента. Примерами имен Swing-компонентов могут
служить J F r a m e , J P a n e l , J A p p l e t , J D i a l o g и J B u t t o n . Каждому AWT-компоненту
соответствует Swing-аналог. Почти для всех компонентов Swing суперклассом являет­
ся J C o m p o n e n t , что обеспечивает возможность настройки внешнего вида компонен­
тов, создания обрамлений, определяемых разработчиком, и окон подсказки.

"Легковесные" компоненты
Большинство компонентов Swing являются "легковесными''. Это значит, что ото­
бражение компонента в окне осуществляется с помощью Java-кода; платформенно-
зависимые средства не используются. Исключениями являются J F r a m e , J A p p l e t ,
JWindow и J D i a l o g . Эти средства являются ^'тяжеловесными". Поскольку графические
изображения, выводимые в буфер, должны отображаться на экране, данные
"тяжеловесные" компоненты представляют собой средства согласования с AWT-
компонентами.

Использование метода paintComponent


П р и работе с компонентами Swing операции рисования выполняются не в p a i n t ,
а в методе p a i n t C o m p o n e n t . По умолчанию p a i n t C o m p o n e n t вызывает представите­
ля пользовательского интерфейса, или UI-представителя (UI— user interface), который
управляет внешним видом компонента. Кроме того, UI-представитель отвечает за
очистку внеэкранного буфера перед рисованием. Поэтому перед выполнением рисо­
вания необходимо вызывать метод p a i n t C o m p o n e n t суперкласса, чтобы обеспечить
очистку буфера и настройку представления компонента. Другими словами, переопре­
деляемый метод p a i n t C o m p o n e n t должен начинаться следующим образом:
p u b l i c void paintComponent(Graphics g){
super.paintComponent(g);
/ / Вывод компонента Swing . . .
1 4 . 1 . О б щ и е сведения о Swing 545

Методика профессионалов

При использовании Swing рисование осуществляется не в методе


paint, а в методе paintComponent. Перед выполнением операций
рисования необходимо вызывать super. paintComponent.

Использование панелей содержимого


Работая со средствами Swing, не следует включать компоненты в контейнер
JFrame или J A p p l e t . Для включения компонентов создается панель содержимого
(компонент C o n t e n t P a n e ) .
Container content = getContentPane();
content.add(new JButton("Welcome"));
content.add(new JLabel("JavaOne");
Попытка поместить компонент непосредственно в JFrame или J A p p l e t приведет к
возникновению ошибки. Панель содержимого представляет собой простой контей­
нер, с которым связан диспетчер компоновки B o r d e r L a y o u t . В Swing с контейнера­
ми J F r a m e и J A p p l e t связаны одинаковые диспетчеры ( B o r d e r L a y o u t ) . Этим они
отличаются от соответствующих средств AWT, где с Frame связан диспетчер
B o r d e r L a y o u t , а с A p p l e t — F l o w L a y o u t . Если вы хотите заменить панель содержи­
мого другим контейнером, вам следует вызвать метод s e t C o n t e n t P a n e .
Методика профессионалов

Добавляя компоненты в контейнер JFrame или JApplet, необходимо


использовать панель содержимого.

Двойная буферизация
По умолчанию J P a n e l поддерживает двойную буферизацию (подробно она будет
описана в главе 16). В Swing единственный внеэкранный буфер, размеры которого
достаточны для поддержки экрана, управляется с помощью R e p a i n t M a n a g e r . При
выполнении метода p a i n t происходит обращение к R e p a i n t M a n a g e r для получения
внеэкранного буфера. Рисование в буфере осуществляется с помощью метода p a i n t ­
Component, а затем содержимое буфера копируется на экран. Вывод в J P a n e l по­
средством p a i n t C o m p o n e n t на самом деле представляет собой вывод во внеэкранный
буфер. При рисовании за пределами метода p a i n t используется вызов p a n e l .
g e t G r a p h i c s ( ) , при этом вывод осуществляется непосредственно на экран и двой­
ная буферизация не выполняется.
К каждому компоненту, помещенному в контейнер с двойной буферизацией, дан­
ный тип буферизации применяется автоматически. Так, поскольку панель содержи­
мого, включаемая в J F r a m e и J A p p l e t , представляет собой контейнер с двойной бу­
феризацией, при работе Java-приложений на базе Swing также используется двойная
буферизация.
546 Глава 1 4 . Основы Swing

Архитектура "модель-просмотр-контроллер"
Структура "легковесных" Swing-компонентов соответствует архитектуре "модель-
просмотр-контроллер" (MVC —model-view-controller), показанной на рис. 14.1. Модель
представляет собой структуру данных, которая предоставляет доступ к информации с
помощью специальных методов. Структура может быть проста и включать несколько
переменных, которые задают состояние объекта, а может быть очень сложной, как,
например, массив, определяющий набор полей в таблице. Просмотр используется для
визуального представления данных модели. Контроллер представляет собой обработ­
чик событий.
С каждым "легковесным" компонентом Swing связан представитель пользователь­
ского интерфейса (UI), который является дочерним объектом ComponentUI. UI-
представитель управляет внешним видом компонента и особенностями обработки
событий. При создании экземпляра компонента UIManager возвращает объект, оп­
ределяющий пользовательский интерфейс (LabelUI, TableUI, TreeUI и т.д.), осно­
ванный на внешнем виде и поведении программы (Windows, Motif, Java). UI-
представитель сообщает минимальный, максимальный и нормальный размеры ком­
понента, отвечает за вывод компонента и поддержку связанных с ним событий.
Преимущество архитектуры MVC состоит в том, что одна и та же модель может
быть связана с различными компонентами, обеспечивая таким образом различные
стили вывода данных. Представьте себе данные, отображаемые в виде таблицы и в
виде гистограммы. Оба типа отображения соответствуют одной модели. Если пользо­
ватель включит в модель новые данные, будет сгенерировано событие, связанное с
моделью. В результате оба представления подвергнутся обновлению. Различные
представления сложных данных часто применяются при работе со средствами Swing.
Так, например, в главе 15 будет приведен пример отображения данных с помощью
объектов JTree и JTable.

оповещение
об обновлении

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

[ Событие J

Рис. 14.1. Архитектура "модель-просмотр-


контроллер" (MVC)
1 4 . 1 . Общие сведения о Swing 547

Внешний вид компонентов


Архитектура MVC позволяет настраивать внешний вид компонентов Swing. В слу­
чае применения Swing пользовательский интерфейс может быть представлен в стиле
Windows, Motif, MacOS или Java (раньше этот стиль назывался Metal). П р и создании
компонента U I M a n a g e r возвращает UI-представителя, который отвечает за отобра­
жение компонента в соответствии с выбранным стилем, поэтому стиль интерфейса
необходимо определить перед созданием "легковесных" компонентов Swing. Ниже
описаны некоторые особенности определения внешнего вида пользовательского ин­
терфейса.

• По умолчанию создается кроссплатформенный стиль Java (или Metal), который


напоминает интерфейсы, используемые в системе Windows.
• Стиль Motif доступен на всех платформах, а Windows и Мае — только на своих
платформах. Это ограничение легко обойти, однако распространение прило­
жений, созданных подобным образом, запрещено.
• Внешний вид интерфейса может изменяться в процессе выполнения програм­
мы. После определения нового стиля надо вызвать S w i n g U t i l t i e s . U p d a t e -
C o m p o n e n t T r e e U I , в результате чего с каждым компонентом будет связан но­
вый UI-представитель. Например:
try {
UIManager. setLoolcAndFeel (
"javax.swing.motif.MotifLookAndFeel");
} catch(Exception e) {
System.out.printlnC'LAF Error: " + e) ;
}
SwingUtilties.updateComponentTreeUI(
getContentPane());
Данная возможность выглядит впечатляюще, но она редко используется на
практике. Обычно внешний вид интерфейса задается в конструкторе класса
J F r a m e , в методе m a i n или в методе i n i t объекта J A p p l e t .
• При создании интерфейса можно сделать так, что он будет выглядеть подобно
обычному интерфейсу, типичному для данной платформы. Для этого надо вы­
звать метод g e t S y s t e m L o o k A n d F e e l C l a s s N a m e объекта U I M a n a g e r и пере­
дать результаты вызова методу U I M a n a g e r . s e t L o o k A n d F e e l . Поскольку
s e t L o o k A n d F e e l может генерировать исключение, непосредственное исполь­
зование описанного способа не совсем удобно. Лучше создать статический ме­
тод вспомогательного класса с именем s e t N a t i v e L o o k A n d F e e l .
В листинге 14.1 показан код вспомогательного класса W i n d o w U t i l i t i e s . В нем
содержатся статические методы, которые устанавливают внешний вид, соответст­
вующий используемой платформе, стилю Java (Metal) и Motif. Большинство пользова­
телей предпочитают работать с интерфейсом, соответствующим конкретной плат­
форме, так как многие из них не привыкли к интерфейсу Java (этот стиль установлен
548 Глава 14. Основы Swing

по умолчанию в JRE). Поэтому в начале выполнения программы желательно устано­


вить стиль, типичный для используемой платформы.
Класс WindowUtilities содержит также статический метод, предназначенный
для отображения контейнеров в JFrame. Различные методы open In JFrame исполь­
зуются в данной главе для вывода окон JPanel в JFrame. Обработчик ExitListener
применяется для завершения приложения при закрытии главного окна.

Листинг 14.1. WindowUtilitiesоava

import javax.swing.*;
import java.awt.*; // Для классов Color и Container.
/** Некоторые утилиты, упрощающие использование окон в Swing. */
public class WindowUtilities {
/** Системе сообщается о том, что стиль должен соответствовать
типичному интерфейсу конкретной платформы. По умолчанию
устанавливается стиль Metal (Java).
*/
public static void setNativeLookAndFeel() {
try {
UIManager.setLookAndFeel(
UIManager. getSystemLookAndFeelClassName ()) ;
} catch(Exception e) {
System.out.println("Error setting native LAF: " + e ) ;
}
}

public static void setJavaLookAndFeel() {


try {
UIManager.setLookAndFeel(
UIManager.getCrossPlatformLookAndFeelClassName());
} catch(Exception e) {
System.out.println("Error setting Java LAF: " + e ) ;
}
}
public static void setMotifLookAndFeel() {
try {
UIManager.setLookAndFeel(
"com.sun.j ava.swing.plaf.motif.MotifLookAndFeel");
} catch(Exception e) {
System.out.println("Error setting Motif LAF: " + e ) ;
}
}

/** Упрощенный способ отображения JPanel или другого контейнера.


* На экран выводится JFrame с указанным объектом Container
* в качестве панели содержимого.
V
public static JFrame openlnJFrame(Container content,
int width,
int height,
String title.
Color bgColor) {
1 4 . 1 . Общие сведения о Swing 549

JFrame frame = new JFrame(title);


frame.setBackground(bgColor);
content.setBackground(bgColor) ;
frame.setSize(width, height);
frame.setContentPane(content);
frame.addWindowListener(new ExitListener());
frame.setVisible(true);
return(frame);
}
/** В качестве цвета фона используется Color.white. */

public static JFrame openlnJFrame(Container content,


int width,
int height.
String title) {
return(openlnJFrame(content, width, height,
title. Color.white));
}
Z'^* Color.white используется в качестве цвета фона, а
* имя контейнера в качестве заголовка JFrame.
V
public static JFrame openlnJFrame(Container content,
int width,
int height) {
return(openlnJFrame(content, width, height,
content.getClass().getName(),
Color.white));

Листинг 14.2.ExitListener.java

import java.awt.*;
import Java.awt.event.*;
/** Обработчик, который связывается с компонентом верхнего
* уровня JFrame приложения, так, что при закрытии окна
* приложение завершается.
V
public class ExitListener extends WindowAdapter {
public void windowClosing(WindowEvent event) {
System.exit(0);
}

В состав Java 2 JDK включен аплет SwingSet2, демонстрирующий различные ком­


поненты Swing. Этот аплет расположен в каталоге r o o t / j d k l . 3/demo/j f с /
SwingSet2. На рис. 14.2— 14.4 показаны варианты окна аплета, демонстрирующие
соответственно стили Windows, Motif и Java.
550 Глава 14. Основы Swing

ill kWf^f^
Table D$mo | Source Code |

P Reprderlno ««fowed - Ssleefion mode


Г" Column seiettjon
P" Hoftz. Une&' jMuitiple ranges ^|
P Row selection
F Ven. Unes
Rowueight ' A«tere«i2e mod©
Mtt^f'SdK трасте;:
__j. jSubsequent columns ;;^J
-J
first Name I UetNeme FgvoHte Kovie I Fevortte Number I f»voirlte
Mike Brazil
jMarK :Curseotthe Demon
JBrian |The Bluws Brothers
iLara Airplane (the whole series)
Roger The ManVvrno Kne^vToo Much

Brent •Bintie Runner (Director's Cut)


jMark Brazil
jjeir The Lady Vanishes
lEwan A Bug's Life
[Anny Reser\ruirDcg£

Рис. 14.2. Интерфейс аплета SwingSet2 в стиле Windows

im у>0н^¥ш

m оm
Table l>ew<?s

• Selectkw wo<ie
J column sefecfeon
Мий||^егерзе$ J' /1>
IS? Roweelec^cm
i ? У М Уwe Autore^is&rmjde
Rownelgbt
ir^r-teiupdesi$;: .

¥^Фт1лтб I fmom i^gftNr

|Tt)^BHie«8rpthers
iart« фш ^тш eerjes)
ыт Who i^swTtHt^ imuth
|8lade Шпт* ф|гесЮг^ С^й>

[•теи<1уУаб1§г»«
Д0«в'еУГе ^ '^

Рис. 14.3. Интерфейс аплета SwingSet2 в стиле Motif


1 4 . 1 . Общие сведения о Swing 551

fee took&Feei Themes

5 Р ( * : л*-;-:Г~'
mm щ.-=^-\ 1И
Ti№leOemoMir^.^1

! S^lfeCtmu mutie
к. Reorctefbio alowed

v H o r k , Lines
!«i Row «Section
'< Veit.Unes rAiitw»$Jift?niMid8'"
c:3rrdlj^)br

First Name ; Last Name ! Favorite C(Hor Favorite Movie Favortte Number i FgvorlteFood
Mii-e Albers sBrazil

Mark Andrews Curse of the Demon


Brian Beck The Blues Brothers
Lara Bunni [Airplane (the whole senes)
Roger Brinkley The Man Who Knew Too Much
Brent Chnstian Blade Runner (Director's Cut)
Mark Davidson Brazil
Jeff Dinkins The Lady Vanishes
Ewan Dinkins A Bug's Life
Amy Fowler Reservoir Dogs

P*fc. 14.4. Интерфейс аплета SwingSet2 в стиле Java (Metal)

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


AWT и Swing
Компоненты AWT всегда располагаются поверх остальных компонентов. На
платформе Java существует понятие z-npuopumema, или z-расположенгся, с помощью ко­
торого описывается порядок отображения элементов, размещенных в одном контей­
нере. Первый из компонентов, включаемый в контейнер, получает наивысший z-
приоритет, для последнего компонента z-приоритет имеет самое низкое значение.
Компоненты с высоким значением z-приоритета располагаются поверх компонентов
с более низким z-приоритетом. Соответственно окно-контейнер всегда имеет низший
z-приоритет и отображается ниже всех других тяжеловесных компонентов. Легковес­
ные Swing-компоненты всегда располагаются в тяжеловесных контейнерах и получа­
ют тот же z-приоритет, что и контейнер. В результате компоненты AWT всегда ото­
бражаются поверх компонентов Swing. Такое поведение интерфейсных элементов
может привести к неожиданным результатам во время выполнения программы.
Предположим, что в Swing-приложении используются раскрывающийся список и
объект AWT Button. В зависимости от расположения элементов кнопка может ото­
бражаться поверх выбранного раскрывающегося списка. Наилучший способ изба­
виться от подобных э ф ф е к т о в — использовать в одной программе либо только компо­
ненты AWT, либо только компоненты Swing.
Перед тем как приступать к рассмотрению различных компонентов Swing, заме­
тим, что в классе j a v a x . s w i n g . S w i n g C o n s t a n t s определены многочисленные кон­
станты, предназначенные для позиционирования компонентов. В частности, разра­
ботчику доступны константы LEFT, CENTER, RIGHT, TOP, BOTTOM, NORTH, EAST,
SOUTH, WEST, NORTH EAST, NORTH WEST, SOUTH EAST, SOUTH WEST, HORIZONTAL,
552 Глава 1 4 . Основы Swing

VERTICAL, LEADING и TRAILING. Последние две константы определяют начало или


конец компонента в соответствии с порядком чтения, принятым для конкретной на­
циональной кодировки (слева направо и справа налево).

14.2. Компонент JApplet


J A p p l e t представляет собой Swing-экраневивалент AWT-компонента A p p l e t . Ос­
новное ограничение использования J A p p l e t связано с тем, что среди популярных
броузеров средства Java 2 Platform поддерживает только Netscape 6. Вы можете орга­
низовать работу с элементами Swing в броузерах, поддерживаемых Java 1.1, но для то­
го, чтобы в полной мере воспользоваться возможностями Swing, мы рекомендуем
средства Java 2. Чтобы Swing-аплет выполнялся в броузере, необходимо инсталлиро­
вать продукт Java 2 Plug-In, описанный в главе 9. Поскольку объем пакета Java Plug-In,
который должен быть скопирован по сети, превышает 5 Мбайт, Swing-аплеты исполь­
зуются в Internet достаточно редко. Такое решение пригодно лишь для сетей intranet,
где пакет Java Plug-In может быть быстро скопирован с локального сервера.
J A p p l e t — тяжеловесный Swing-компонент, суперклассом которого является
A p p l e t . Этим он отличается от "легковесных" компонентов Swing, которые пред­
ставляют собой подклассы класса J C o m p o n e n t . О т своего суперкласса J A p p l e t унас­
ледовал знакомые вам методы i n i t , s t a r t , s t o p и d e s t r o y . Поскольку J A p p l e t яв­
ляется компонентом Swing, между ним и объектом A p p l e t существует ряд различий.

• J A p p l e t включает панель содержимого, в которую добавляются компоненты.


Изменения таких характеристик, как используемый диспетчер компоновки,
цвет ф о н а и т.д., применяется к панели содержимого. Доступ к этой панели
осуществляется с помощью метода g e t C o n t e n t P a n e .
• С J A p p l e t (как и с контейнерами Frame и JFrame) по умолчанию связан диспетчер
компоновки B o r d e r L a y o u t . Для сравнения, размещением компонентов в контей­
нере A p p l e t по умолчанию занимается диспетчер FlowLayout. Если говорить
строго, диспетчер B o r d e r L a y o u t реально связан с панелью содержимого.
• По умолчанию реализуется интерфейс в стиле J a v a ( M e t a l ) . П р и желании вы
можете явно указать, что создаваемый вами и н т е р ф е й с должен выглядеть по­
добно интерфейсам других программ на данной платформе.
• Рисование выполняется не в методе p a i n t , а в методе p a i n t C o m p o n e n t . Вме­
сто того чтобы выводить графическую информацию непосредственно в
J A p p l e t , следует создать контейнер J P a n e l , добавить его в панель содержи­
мого, переопределить метод p a i n t C o m p o n e n t объекта J P a n e l и выполнять
рисование с помощью этого метода.
• Двойная буферизация разрешена по умолчанию.

М е т о д и к а профессионалов

В отличие от компонента Applet, с JApplet по умолчанию связыва­


ется диспетчер компоновки BorderLayout.
14.3. Компонент JFrame 553

В листинге 14.3 показан простой пример Swing-аплета. Выглядит этот аплет так же,
как и AWT-аплет, в процессе работы которого не выполняются никакие действия, кроме
включения в окно трех кнопок, В Swing-версии для достижения аналогичного результа­
та диспетчер компоновки, используемый по умолчанию, заменен на FlowLayout. На
рис. 14.5 показаны результаты выполнения аплета в среде броузера Netscape 6.

Листинг 14.3. JApplGtExample.Java

import java.awt.'^;
import javax.swing.*;
/** Небольшой пример, демонстрирующий различия в использовании
* JApplet и Applet: в окно включается панель содержимого,
* по умолчанию установлен стиль интерфейса Java (Metal),
* а размещением компонентов управляет диспетчер компоновки
* BorderLayout (вместо FlowLayout).
V
public class JAppletExample extends JApplet {
public void initO {
WindowUtilities.setNativeLookAndFeel();
Container content = getContentPane();
content.setBackground(Color.white);
content.setLayout(new FlowLayout());
content.add(new JButton("Button 1"))
content.add(new JButton("Button 2")),
content.add(new JButton("Button 3")),

шюшшшшт
File Ed» ^ i e w Search Qp Book'narks lasks jHelp

Рис. 14.5. Swing-компоненты J B u t t o n ,


отображаемые в окне броузера Netscape 6,
QiM€>:200(XmM2S который выполняется в системе Windows 98

14.3. Компонент JFrame


JFrame — это Swing-эквивалент AWT-объекта Frame. Как и при работе с AWT,
JFrame является базовым компонентом для создания графического приложения. По­
добно JApplet, JFrame представляет собой тяжеловесный Swing-компонент, для ко­
торого JComponent не является суперклассом. В роли суперкласса для JFrame высту­
пает Frame. Основные отличия между JFrame и AWT-компонентом Frame перечис­
лены ниже.
554 Глава 14. Основы Swing

• Компоненты нельзя включать непосредственно в окно J F r a m e ; вместо этого


они включаются в панель содержимого, которая входит в состав J F r a m e . Заме­
на диспетчера компоновки, изменение цвета ф о н а и других характеристик от­
ражается на панели содержимого. Панель содержимого доступна посредством
getContentРапе.
• В отличие от Frame, J F r a m e автоматически закрывается при активизации
пользователем соответствующей кнопки. Однако это совсем не означает, что
указанное действие пользователя приведет к завершению Java-приложения.
Поэтому с главным окном J F r a m e программы необходимо связать обработчик
W i n d o w L i s t e n e r , в котором должен вызываться метод S y s t e m , e x i t . При ис­
пользовании JDK 1.3 вы можете вызвать метод s e t D e f a u l t C l o s e O p e r t i o n
( E X I T O N C L O S E ) . Однако в этом случае невозможно выполнить специальные
действия по закрытию приложения, как это можно сделать при использовании
WindowListener.
• По умолчанию реализуется и н т е р ф е й с в стиле Java (Metal). П р и желании вы
можете явно указать, что создаваемый вами интерфейс должен выглядеть по­
добно интерфейсам других программ на данной платформе.

Методика профессионалов
Дочерние окна автоматически закрываются при активизации соот­
ветствующей кнопки, однако с главным окном JFrame следует свя­
зать обработчик WindowListener. В процессе обработки события
необходимо предусмотреть вызов System, exit.

В листинге 14.4 показан код простого приложения. Оно выглядит на экране так
же, как и AWT-приложение на основе объекта Frame, в котором с окном связывается
диспетчер компоновки F l o w L a y o u t , а в окно включаются три кнопки. Результаты
выполнения приложения показаны на рис. 14.6. Коды W i n d o w U t i l i t i e s . J a v a и
E x i t L i s t e n e r . J a v a были рассмотрены в начале данной главы. Их можно скопиро­
вать, обратившись по адресу h t t p : / /www. c o r e w e b p r o g r a m m i n g . com/.

Листинг 1 4 . 4 . JFrameExample. J a v a

import java.awt.*;
import javax.swing.*;
/** Небольшой пример, демонстрирующий различия в использовании
* JFrame и Frame: в окно включается панель содержимого,
* по умолчанию вместо стиля, типичного для используемой
* платформы, установлен стиль интерфейса Java (Metal).
V
public class JFrameExample {
public static void main(String[] args) {
WindowUtilities . setNativeLoolcAndFeel () ;
JFrame f = new JFrame("This is a test");
f.setSize(400, 150);
Container content = f.getContentPane();
14.4. Компонент JLabel 555

content.setBackground(Color.white);
content.setLayout(new FlowLayout()) ;
content.add(new JButton("Button 1")),
content.add(new JButton("Button 2")),
content.add(new JButton("Button 3")),
f.addWindowListener(new ExitListener()
f.setVisible(true);

I LP-4^?!yj--'i

Рис. 14.6. Swing-компонент JFrame,


содержащий три кнопки

14.4. Компонент JLabel


Подобно L a b e l , компонент J L a b e l используется для отображения текста. Одна­
ко, в отличие от L a b e l , J L a b e l предоставляет разработчику дополнительные воз­
можности. Во-первых, в дополнение к тексту или вместо текста может выводиться
изображение, во-вторых, вокруг данного компонента может отображаться рамка и, в-
третьих, для форматирования текста можно использовать HTML-код.

Новые средства: изображения, рамки


и НТМЬ'СОдержимое
Первое из новых средств, реализованных в объекте J l a b e l , — это возможность
выводить графические изображения (пиктограммы) посредством Image I c o n . Обыч­
но вывод изображения задается в конструкторе класса либо реализуется с помощью
вызова метода s e t I c o n . Пиктограммы в объекте J L a b e l используются так же, как и в
объектах J B u t t o n , которые будут обсуждаться в следующем разделе. Там же будут
подробно рассмотрены особенности отображения пиктограмм и приведены приме­
ры. П р и м е р использования J L a b e l с пиктограммой также представлен в листинге
14.5. Несмотря на то что классы J L a b e l и J B u t t o n предоставляют разработчику
сходные средства, они не связаны через наследование.
Вторая возможность, предоставляемая разработчику, использующему J l a b e l , —
отображение рамки вокруг компонента. Рамка вокруг J L a b e l создается аналогично
рамке вокруг компонента J P a n e l , который будет рассмотрен далее в этой главе.
Здесь же приводится лишь простой пример использования рамок (листинг 14.5).
Основное внимание в данном разделе мы уделим третьей возможности — форма­
тированию текста средствами HTML. Если строка, содержащаяся в данном компо­
ненте, начинается с последовательности символов "<html>", она рассматривается не
как набор литеральных символов, а как HTML-код. Возможность интерпретации
HTML-фрагментов позволяет объединять в одном компоненте разные цвета и шриф-
556 Глава 14. Основы Swing

ты и обеспечивает другие э ф ф е к т ы . HTML-код также может использоваться в объекте


J B u t t o n . Несмотря на удобство, на применение HTML накладываются некоторые
ограничения.

• Поддержка HTML объектами J L a b e l реализована только в JDK L2.2, Swing


L L 1 или более поздних версиях этих пакетов. Поскольку платформа Java не
предоставляет средств, позволяющих проверить, поддерживается ли данная
возможность, использование объектов J L a b e l с HTML-содержимым может
стать источником проблем при переносе программ.
• В JDK L2 HTML-код объекта J L a b e l должен начинаться с последовательности
символов "<html>", но не с "<HTML>". Поддержка HTML-дескрипторов, не за­
висящая от регистра символов, реализована только B J D K L 3 .
• BJDK L2 для включения изображения в объект J L a b e l необходимо использо­
вать Image I c o n в конструкторе J L a b e l либо вызывать s e t I c o n . HTML-код не
может содержать дескриптор <img>. B J D K L3 HTML-интерпретатор поддер­
живает работу с изображениями, однако рассмотрение этого вопроса выходит
за рамки данной книги.
• Если в объекте J L a b e l используется HTML-код, JLabel-шрифты игнорируются.
Управление шрифтами должно осуществляться посредством HTML-
дескрипторов. Так, например, глядя на следующий фрагмент кода, можно
предположить, что текст будет отображаться полужирным шрифтом Serif
большого размера, но вместо этого он выводится полужирным шрифтом Sans
Serif хмалого размера:
JLabel label =
new J L a b e l ( " < h t m l > B o l d T e x t < / h t m l > " ) ;
label.setFont(new Font("Serif",Font.BOLD,36))/

• Для включения в текст разрыва строки надо использовать дескриптор <Р> вме­
сто <BR>. В отличие от "реального" HTML, дескриптор <BR> игнорируется, а
<Р> действует как <BR>.
• О поддержке прочих НТМТ-конструкций нельзя сказать ничего определенно­
го. Перед тем как использовать HTML-фрагмент в программе, обязательно
проверьте, как он отображается. Если вы позволите пользователю вводить
HTML-код в процессе выполнения программы, это может привести к возник­
новению проблем.

Конструкторы JLabel
в классе J L a b e l предусмотрено семь конструкторов. Ч е т ы р е из них, которые ис­
пользуются наиболее часто, описаны ниже.
14.4. Компонент JLabel 557

public JLabelO
public JLabel(String label)
public JLabel(Icon image)
public JLabel(String label, I c o n image, int hAlignment)
Указанные конструкторы позволяют создать пустой объект J L a b e l , объект
J L a b e l , отображающий фрагмент текста, и объект J L a b e l с изображением. По­
следний из приведенных конструкторов отображает как пиктограмму, так и текст.
Параметр h A l i g n m e n t позволяет задавать горизонтальное выравнивание пары
"текст-пиктограмма" относительно самого объекта J L a b e l . Допустимыми значе­
ниями данного параметра являются LEFT, CENTER, RIGHT, LEADING и TRAILING.

Методы JLabel
Н и ж е представлены наиболее часто используемые методы класса J L a b e l . Каждо­
му из методов, устанавливающих значение свойства, соответствует метод, позволяю­
щий определить установленное значение (get-метод).

public void s e t H o r i z o n t a l A l i g n m e n t ( i n t alignment)


public void setVerticalAlignment(int alignment)
Данные методы позволяют задавать тип горизонтального и вертикального вырав­
нивания пары "текст-пиктограмма" относительно объекта J L a b e l . Типы вырав­
нивания указываются с помощью констант, определенных в классе j a v a x . s w i n g .
S w i n g C o n s t r a i n t s : LEFT, CENTER, RIGHT, LEADING и TRAILING для горизонталь­
ного выравнивания; TOP, CENTER и BOTTOM — для вертикального выравнивания.

public void s e t H o r i z o n t a l T e x t P o s i t i o n ( i n t alignment)


public void setVerticalTextPosition(int alignment)
Данные методы задают горизонтальную и вертикальную позиции текста относи­
тельно изображения. Допустимые значения параметра совпадают со значениями
для пары "текст-пиктограмма".

public void s e t I c o n ( I c o n image)


public void s e t D i s a b l e d I c o n ( I c o n image)
Указанные два метода задают изображение для разрешенного и запрещенного ком­
понентов J L a b e l . Для запрета J L a b e l используется вызов s e t E n a b l e d ( f a l s e ) .

public void setText(String label)


public void setFont(Font font)
Метод s e t T e x t задает фрагмент текста, который должен отображаться посредст­
вом J L a b e l . В тексте могут содержаться HTML-дескрипторы, помещенные между
< h t m l > и < / h t m l > . Метод s e t F o n t задает ш р и ф т для компонента J L a b e l .
558 Глава 14. Основы Swing

Примеры использования новых возможностей JLabel приведены в листинге 14.5.


Результат выполнения кода показан на рис. 14.7. Первый и второй компоненты JLabel
используют HTML-код для управления цветом и шрифтами. Последний компонент со­
держит изображение и HTML-код. В составе HTML-фрагмента используются дескрип­
торы <UL>. Вокруг каждого из компонентов отображается рамка с заголовком. Подроб­
нее о выводе рамок будет сказано ниже, при обсуждении компонента JPanel.

Листинг 14.5. JLabels.java

import java.awt.*;
import javax.swing.*;
/** Небольшой пример, демонстрирующий использование JLabel,
* в частности поддержку HTML (только Swing 1.1,1, Java 1.2.2
* и более поздние версии).
Ч
public class JLabels extends JFrame {
public static void main(String[] args) {
new JLabels();

public JLabels0 {
superC'Using HTML in JLabels");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
Font font = new Font("Serif", Font.PLAIN, 30);
content.setFont(font);
String- labelText =
"<htmlXFONT COLOR=WHITE>WHITE</FONT> and " +
"<FONT COLOR=GRAY>GRAY</FONT> Text</html>";
JLabel coloredLabel =
new JLabel(labelText, JLabel.CENTER);
coloredLabel.setBorder
(BorderFactory.createTitledBorder("Mixed Colors"));
content.add(coloredLabel, BorderLayout.NORTH);
labelText =
"<htmlXB>Bold</B> and <I>Italic</l> Text</html>";
JLabel boldLabel =
new JLabel(labelText, JLabel.CENTER);
boldLabel.setBorder
(BorderFactory.createTitledBorder("Mixed Fonts"));
content.add(boldLabel, BorderLayout.CENTER);
labelText =
"<html>The Applied Physics Laboratory is a division " +
"of the Johns Hopkins University." +
"<P>" +
"Major JHU divisions include:" +
"<UL>" +
" <LI>The Applied Physics Laboratory" +
" <LI>The Krieger School of Arts and Sciences" +
" <LI>The Whiting School of Engineering" +
" <LI>The School of Medicine" +
14.5. Компонент JButton 559

" <LI>The School of Public Health" +


" <LI>The School of Nursing" +
" <LI>The Peabody Institute" +
" <LI>The Nitze School of Advanced International Studies" +
"</UL>" +
"</html>";
JLabel fancyLabel =
new JLabel(labelText,
new Imagelcon("images/JHUAPL.gif"),
JLabe1.CENTER);
fancyLabel.setBorder
(BorderFactory.createTitledBorder("Fancy HTML"));
content.add(fancyLabel, BorderLayout.SOUTH);
packO ;
setVisible(true);
}

.rJPJXl

and •:"./> TeKt

Bold and Italic Test


Fancy HTML

The Applied Physics Laboraloiy is a division of the Johns Нсфкш$ Umversity.


Wfejor JHU divisions include"

• The Appliei Physics Laboratory


..0^^/*
• The Kjriegcr School of Arts and Sciences
• The Whrting School of Engineering
• The School of Medicine
• The School of Public Health
• The School of Nursing
• The Peabody kistitute
• The Низе School of Advanced Itatcrnational Studies

Рис. 14.7. Компоненты Swing обеспечивают поддержку


HTML-кода

14.5. Компонент JButton


Swing-компонент J B u t t o n может использоваться так же, как и AWT-компонент
Button. Вы создаете объект JButton, определяя надпись с помощью объекта
S t r i n g , а затем включаете кнопку в окно. События также обрабатываются аналогич­
но классу Button; с объектом J B u t t o n с помощью метода a d d A c t i o n L i s t e n e r свя­
зывается обработчик A c t i o n L i s t e n e r .

Новые возможности: пиктограммы,


tt 99
выравнивание и 'торячие клавиши
Одно из основных отличий J B u t t o n от AWT-компонентов Button — возможность
отображения пиктограмм на кнопках. Sing предоставляет вспомогательный класс
560 Глава 14. Основы Swing

Image I con, с помощью которого можно легко описывать файлы изображений (JPEG,
GIF, а также анимационный GIF). Проще всего связать изображение с JButton, пере­
давая объект Imagelcon конструктору вместо текста либо вместе с текстом. С J B u t t o n
может быть связано семь различных изображений.
1. Основное изображение (если изображение не передано конструктору, его
можно задать с помощью метода s e t Icon).
2. Изображение, используемое при нажатии кнопки ( s e t P r e s s e d l c o n ) .
3. Изображение, которое выводится при помещении курсора мыши на кнопку
( s e t R o l l o v e r l c o n ; заметьте, что сначала надо вызвать метод
setRolloverEnabled(true)).
4. Изображение для запрещенной кнопки ( s e t D i s a b l e d l c o n ) .
5. Изображение, которое используется тогда, когда кнопка выбрана и разрешена
(setSelectedlcon).
6. Изображение, которое используется тогда, когда кнопка выбрана и запрещена
(setDisabledSelectedlcon).
7. Изображение, используемое при помещении курсора мыши на выбранную
кнопку ( s e t R o l l o v e r S e l e c t e d l c o n ) .
Изображения для выбранной кнопки ( s e t S e l e c t e d l c o n , s e t R o l l o v e r S e l e c ­
t e d l c o n и s e t D i s a b l e d S e l e c t e d l c o n ) не поддерживаются B J D K 1.2; их поддержка
реализована только в JDK 1.3. Выбрать кнопку можно из программы, вызвав метод
s e t S e l e c t e d ( t r u e ) . Выбор кнопки не приводит к генерации события
ActionEvent. Если вы не создаете кнопку с поддержкой состояния, вы можете ис­
пользовать стандартный объект JToggleButton (см. документацию по API, описание
j a v a x . swing . JToggleButton), который допускает выбор как из программы, так и в
результате действий пользователя.
При создании интерфейса вы можете измеиигь выравнивание текста, пиктограм­
мы либо пары "текст-пиктограмма", используя для этого методы s e t H o r i z o n t a l -
Alignment и s e t V e r t i c a l A l i g n m e n t (выравнивание возможно только в том случае,
если размеры компонента превышают его нормальные размеры). Вы также можете
изменить позицию текста относительно пиктограммы с помощью методов
s e t H o r i z o n t a l T e x t P o s i t i o n и s e t V e r t i c a l T e x t P o s i t i o n . Интервал между тек­
стом и пиктограммой задается вызовом s e t l c o n T e x t G a p .
Компонент J B u t t o n позволяет определять "горячие клавиши" для активизации
кнопок. Это осуществляется с помощью метода setMnemonic. Если "горячая клави­
ша" задана, один из символов в надписи на кнопке отображается с подчеркиванием, а
кнопка активизируется посредством комбинации клавиш <Ак+символ>.

HTML'КОД для создания надписей на кнопках


В JDK 1.2.2, Swing 1.1.1 и более поздних версиях данных пакетов реализована воз­
можность использования HTML в тексте, задаваемом для компонентов J B u t t o n и
JLabel. Вы можете определять фрагмент текста, состоящий из нескольких строк,
использовать различные шрифты и цвета и реализовывать при создании кнопок про­
чие эффекты.
14.5. Компонент JButton 561

Конструкторы JButton
в классе JButton предусмотрено пять конструкторов:

public JButtonO
public JButton(String label)
public J B u t t o n ( I c o n image)
public JButton(String label, I c o n image)
public JButton(Action action)
Первые четыре конструктора создают соответственно пустой компонент J B u t t o n ,
компонент J B u t t o n с надписью, компонент J B u t t o n с пиктограммой и компонент
J B u t t o n с надписью и пиктограммой. Пятый конструктор реализован B J D K 1.3. Ему
передается объект A c t i o n , предназначенный для разделения информации о со­
стоянии с другими компонентами.

Методы классов JButton и AbstractButton


в самом классе JButton определено лишь несколько методов. Однако в нем реали­
зовано большое количество абстрактных методов, объявленных в классе Abstract-
Button. Наиболее часто используемые методы описаны ниже. Каждому set-мeтoд^^
предназначенному для установки значения свойства, соответствует get-метод, позво­
ляющий получить это значение.

public void setAction(Action action)


public Action getAction()
Эти два метода были реализованы в JDK 1.3. Они дают возможность установить и
получить объект Action. С помощью этого объекта можно определить текст и пик­
тограмму для кнопки. Крохме того, в нем содержится метод a c t i o n P e r f o r m e d .
Объект A c t i o n дает возможность разделять информацию о состоянии между раз­
личными компонентами и организовать совместную обработку событий. Так, на­
пример, вы можете посредством A c t i o n связать кнопку в контейнере и кнопку на
панели инструментов.

public void s e t H o r i z o n t a l A l i g n m e n t ( i n t alignment)


public void setVerticalAlignment(int alignment)
Данные методы задают горизонтальное и вертикальное выравнивание пары "текст-
пиктограмма" относительно компонента J B u t t o n . Для указания типов выравнива­
ния применяются те же константы, что и при работе с компонентом J L a b e l .

public void s e t H o r i z o n t a l T e x t P o s i t i o n ( i n t alignment)


public void setVerticalTextPosition(int alignment)
Данные методы позволяют определить позицию текста относительно изображе­
ния на кнопке как по горизонтали, так и по вертикали. Допустимые значения па­
раметров совпадают со значениями для компонента J L a b e l .
562 Глава 14. Основы Swing

public void setText(String label)


public void setFont(Font font)
Метод s e t T e x t задает текст, который должен быть выведен на кнопке, а метод
s e t Font определяет шрифт для отображения текста. Фрагмент текста может
включать HTML-дескрипторы, помещенные между <html> и </html>.

public void setIcon(Icon image)


public void setPressedIcon(Icon image)
public void setRolloverIcon(Icon image)
public void setDisabledIcon(Icon image)
Первый метод задает изображение, связанное с объектом JButton. Остальные
три метода определяют соответственно пиктограмму, отображаемую после щелч­
ка на кнопке, пиктограмму, которая выводится при помещении курсора мыши на
кнопку, и пиктограмму для запрещенной кнопки. Для того чтобы запретить
JButton, надо вызвать метод s e t E n a b l e d ( f a l s e ) .

public void setEnabled(boolean state)


Метод s e t E n a b l e d разрешает ( t r u e ) или запрещает ( f a l s e ) кнoпк)^ По умолча­
нию кнопка разрешена.

public void setMargin(Insets m^argins)


Данный метод определяет расстояние между содержимым кнопки и ее границами.
Например, следующие выражения задают расстояние в 10 пикселей сверху и снизу
текста и расстояние в 5 пикселей слева и справа.
JButton button = new JButton("Continue");
button.setMargin(new Insets(10,5,10,5));
В листинге 14.6 показан код, который создает три основных типа кнопок: кнопку с
текстом, кнопку с изображением и кнопку, содержащую текст и пиктограмму. Резуль­
таты выполнения кода показаны на рис. 14.8.

Листинг 14.6.JButtons.Java

import java.awt.*;
import javax.swing.*;

/** Простой пример, демонстрирующий использование JButton.


* Новые конструкторы позволяют добавлять изображение к кнопке.
V
public class JButtons extends JFrame {
public static void main(String[] args) {
new JButtons{);
}
public JButtons0 {
super("Using JButton");
WindowUtilities.setNativeLookAndFeel();
14.6. Компонент JPanel 563

addWindowListener(new ExitListener());
Container content = getContentPane();
content.setBackground(Color.white);
content.setLayout(new FlowLayout());
JButton buttonl = new JButtonC'Java") /
content.add(buttonl) ;
Imagelcon cup = new Imagelcon("images/cup.gif");
JButton button2 = new JButton(cup);
content.add(button2);
JButton buttons = new JButton("Java", cup);
content.add(button3);
JButton button4 = new JButton("Java", cup);
button4.setHorizontalTextPosition(SwingConstants.LEFT);
content.add(button4);
packO ;
setVisible(true) ;

Рис, / 4 . e . Swing-компонент JButton


поддерживает как текст, так и изображения

14.6. Компонент JPanel


В простейшем случае J P a n e l используют так же, как и AWT-компонент P a n e l .
Разработчик создает панель, включает в нее компоненты, а затем включает компо­
нент в контейнер. Однако J P a n e l может также выполнять функции C a n v a s (объект
J C a n v a s не существует). П р и использовании J P a n e l в качестве области рисования
вместо C a n v a s необходимо выполнить дополнительные действия. Во-первых, надо
установить размер компонента с помощью метода s e t P r e f e r r e d S i z e (размеры
P a n e l и J P a n e l определяются исходя из размеров включенных в них компонентов).
Во-вторых, для рисования вместо метода p a i n t следует использовать метод p a i n t -
Component. Поскольку по умолчанию двойная буферизация разрешена, первое, что
надо сделать в теле метода p a i n t C o m p o n e n t , — очистить внеэкранный буфер, для
этого следует вызвать метод s u p e r . p a i n t C o m p o n e n t .

public void paintComponent(Graphics g) {


super.paintComponent(g);

Используя Swing в Java 2, вы можете преобразовать объект G r a p h i c s к типу


G r a p h i c s 2 D и воспользоваться всеми возможностями пакета Java 2D (они описаны в
главе 10).
564 Глава 14. Основы Swing

Конструкторы JPanel
в классе JPanel определены четыре перечисленных ниже конструктора. При работе с
JPanel вы можете задавать диспетчер компоновки не только с помощью метода
s e t La у o u t . Сделать это позволяют также некоторые из конструкторов класса. Все методы
J P a n e l унаследованы от класса JComponent, поэтому в данном разделе мы не будем их
рассматривать. (За подробной информацией обращайтесь к документации по API.)

public JPanel()
public JPanel(LayoutManager manager)
public J P a n e l ( b o o l e a n i s D o u b l e B u f f e r e d )
public JPanel(LayoutManager manager, b o o l e a n isDoubleBuff ered)
Первый конструктор создает объект J P a n e l , связывает с ним диспетчер компо­
новки F l o w L a y o u t и разрешает двойную буферизацию. Остальные три конструк­
тора предназначены для явного определения диспетчера компоновки и управле­
ния двойной буферизацией.

Новая возможность: обрамление


Помимо двойной буферизации в JPanel также появилась возможность отображать
рамку вокруг компонента. По сути, рамки могут выводиться для каждого элемента
J C o m p o n e n t , однако J P a n e l — единственный компонент, в котором оправдана ин­
сталляция новых рамок. Swing поддерживает семь основных типов рамок: T i t l e d -
o r d e r , E t c h e d B o r d e r , B e v e l B o r d e r (обычный вариант и "мягкая" версия). L i n e -
o r d e r , M a t t e B o r d e r , C o m p o u n d B o r d e r и E m p t y B o r d e r . Вы также можете опреде­
лять собственные типы обрамления. Рамка связывается с компонентом посредством
метода s e t B o r d e r . Для создания объекта B o r d e r либо вызывается конструктор клас­
са, либо используется один из методов B o r d e r F a c t o r y . Например:
J P a n e l р = new J P a n e l ( ) ;
p.setBorder(BorderFactory.createTitledBorder("Java"));
Если есть возможность, методы B o r d e r F a c t o r y повторно используют сущест­
вующие объекты B o r d e r , поэтому, прежде чем создавать новый экземпляр B o r d e r ,
постарайтесь воспользоваться доступными методами B o r d e r F a c t o r y .
Методика профессионалов
По возможности используйте для создания объекта Border метод
сгеаteXxxBorder класса BorderFactory.

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


Класс B o r d e r F a c t o r y предоставляет 23 статических метода, предназначенных
для создания различных типов рамок. Класс B o r d e r принадлежит пакету j a v a x .
s w i n g . b o r d e r . В данном пакете также содержится класс A b s t r a c t B o r d e r , реали­
зующий интерфейс B o r d e r . A b s t r a c t B o r d e r является базовым классом для классов
EmptyBorder, T i t l e d B o r d e r , LineBorder, EtchedBorder, BevelBorder, Soft-
14.6. Компонент JPanel 565

B e v e l B o r d e r , M a t t e B o r d e r и C o m p o u n d B o r d e r . Из 23 методов класса B o r d e r -
F a c t o r y наиболее часто используются методы, описанные ниже. Каждый из этих ме­
тодов определен как p u b l i e s t a t i c .

Border createEmptyBorder(int t o p , int left, int b o t t o m , int right)


Данный метод строит объект E m p t y B o r d e r , который создает пустое пространство
вокруг компонента.

Border c r e a t e L i n e B o r d e r ( C o l o r color)
B o r d e r createLineBorder(Color color, int thickness)
Указанные два метода создают объект L i n e B o r d e r с поддержкой цвета. По умол­
чанию толщина рамки составляет один пиксель. Второй конструктор позволяет
непосредственно задавать толщину рамки в пикселях. Класс L i n e B o r d e r также
предоставляет два статических метода: c r e a t e B l a c k L i n e B o r d e r и c r e a t e G r a y -
L i n e B o r d e r . Эти методы предназначены для построения рамок, отображаемых
черной или серой линией толщиной в один пиксель.

T i t l e d B o r d e r createTitledBorder(String title)
T i t l e d B o r d e r c r e a t e T i t l e d B o r d e r ( B o r d e r b o r d e r . String title)
Данные методы создают объект T i t l e d B o r d e r с заголовком, расположенным в
позиции по умолчанию (ТОР) в левой части верхней линии рамки. По умолчанию
рамка имеет вид "гравюры". Чтобы изменить внешний вид обрамления, надо явно
указать тип рамки (это позволяет сделать второй метод). Класс T i t l e d B o r d e r
предоставляет методы s e t T i t l e P o s i t i o n и s e t T i t l e J u s t i f i c a t i o n . Позиция
заголовка задается относительно верхней части рамки с помощью следующих кон­
стант: ABOVE_TOP, ТОР, BELOW_TOP, ABOVE_BOTTOM, BOTTOM и BELOW_BOTTOM.
В методе s e t T i t l e J u s t i f i c a t i o n выравнивание заголовка задается посредст­
вом констант LEFT, CENTER и RIGHT. Вы также можете указать для заголовка цвет
и ш р и ф т с помощью s e t T i t l e C o l o r , s e t T i t l e F o n t и некоторых методов
BorderFactory.

Border createEtchedBorder()
Border c r e a t e E t c h e d B o r d e r ( C o l o r highlight, Color shadow)
Первый из указанных методов создает LOWERED E t c h e d B o r d e r — рамку, которая
имеет вид бороздки толщиной 2 пикселя. Бороздка окрашена в два цвета для соз­
дания эффекта трехмерного элемента. По умолчанию подсветка обозначается
цветом, несколько более светлым по сравнению с цветом фона, а затенение — бо­
лее темным цветом, чем фон. Второй метод позволяет явно указывать цвет для
обозначения подсветки и затенения. Чтобы вместо бороздки отображался выступ,
надо непосредственно создать объект E t c h e d B o r d e r , передавая в качестве пара­
метра значение RAISED.
В дополнение к перечисленным методам B o r d e r F a c t o r y предоставляет другие
статические методы, предназначенные для создания объектов E m p t y B o r d e r , Ы г х е -
Border и E t c h e d B o r d e r . Кроме того, B o r d e r F a c t o r y позволяет создавать различ­
ные типы объекта B e v e l B o r d e r ( c r e a t e B e v e l B o r d e r , c r e a t e R a i s e d B e v e l B o r d e r
566 Глава 14. Основы Swing

и C r e a t e L o w e r e d B e v e l B o r d e r ) объект M a t t e B o r d e r с выводом рамки одного цвета


или с использованием изображения ( c r e a t e M a t t e B o r d e r ) и составное обрамление,
состоящее из двух рамок ( c r e a t e C o m p o u n d B o r d e r ) .
В листинге 14.7 приведен код простого компонента J P a n e l с рамкой L i n e B o r d e r
синего цвета толщиной 2 пикселя. Перед установкой маски задаются размеры
J P a n e l . Ш и р и н а устанавливается равной 400 пикселям, а высота— О пикселям
(высота компонента не имеет значения, поскольку он помещается в область WEST
диспетчера компоновки B o r d e r L a y o u t ) . Кроме того, в данном примере создаются
три панели, в которые помещаются переключатели опций. Вокруг каждой из панелей
с переключателями отображается рамка T i t l e d B o r d e r . Результаты выполнения кода
показаны на рис. 14.9.

Листинг 1 4 . 7 . J P a n e l s . Java

import java.awt.*;
import javax.swing.*;
/** Простой пример, иллюстрирующий использование компонента
* JPanel, в частности возможность связывания рамок.
V
public class JPanels extends JFrame {
public static void main(String[] args) {
new JPanels();
}
public JPanels0 {
superC'Using JPanels with Borders");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
content.setBackground(Color.lightGray);
JPanel controlArea = new JPanel(new GridLayout(3, 1));
String[] colors = { "Red", "Green", "Blue",
"Black", "White", "Gray" };
controlArea.add(new SixChoicePanel("Color", colors));
String[] thicknesses = { "1", "2", "3", "4", "5", "6" };
controlArea.add(new SixChoicePanel("Line Thickness",
thicknesses));
String[] fontSizes = { "10", "12", "14", "18", "24", "36" };
controlArea.add(new SixChoicePanel("Font Size",
fontSizes));
content.add(controlArea, BorderLayout.EAST);
JPanel drawingArea = new JPanel();
// Высота компонента не имеет значения, поскольку он
// помещается в область WEST.
drawingArea.setPreferredSize(new Dimension(400, 0));
drawingArea.setBorder
(BorderFactory.createLineBorder (Color.blue, 2));
drawingArea.setBackground(Color.white);
content.add(drawingArea, BorderLayout.WEST);
pack () ;
setVisible(true) ;
14.7. Компонент JSlider 567

Листинг 14.8.SixChoicePanel.Java

import java.awt.*;
import javax.swing.*;
/** Контейнер Jpanel, содержащий четыре компонента JRadioButton. Ч

public class SixChoicePanel extends JPanel {


public SixChoicePanel(String title. String[] buttonLabels) {
super(new GridLayout(3, 2));
setBackground(Color.lightGray);
setBorder(BorderFactory.createTitledBorder(title));
ButtonGroup group = new ButtonGroup();
JRadioButton option;
int halfLength = buttonLabels.length/2; // Assumes even length
for(int i=0; KhalfLength; 1++) {
option = new JRadioButton(buttonLabels[i]);
group.add(option) ;
add(option);
option = new JRadioButton(buttonLabels[i+halfLength]);
group.add(option) ;
add(option) ;
}
}

||^IHI,lipW!MJyiiffllfpi|^^
Color •j
с pieifli Г Black

(^ Green Г White

^ Blue ^ Gray

Line Thickness
r 1 Г 4

r 2 r 5

r 3 ^ 6

Font Size

^ 1 0 ^18
r 12 r 24

\^\^ ^ 36 ;

Рис. 14.9. С объектами JPanel могут быть связаны


различные типы рамок

14.7. Компонент JSlider


В AWT компонент S c r o l l b a r имеет два назначения: он используется в качестве
линейного регулятора для выбора числовых значений, а также для управления про­
круткой содержимого окна. Такая "универсальность" приводит к тому, что внешний
вид линейного регулятора оказывается далеким от совершенства. Swing предоставля­
ет разработчику реальный линейный регулятор— компонент J S l i d e r . Объект
J S l i d e r создается так же, как и объект S c r o l l b a r : сначала с помощью конструкто-
568 Глава 14. Основы Swing

pa, вызываемого без параметров, строится горизонтальный линейный регулятор, для


которого по умолчанию устанавливается диапазон от О до 100 и начальное значение,
равное 50. Разработчику также доступны конструкторы, позволяющие задавать ори­
ентацию компонента (для этого используются константы J S l i d e r .HORIZONTAL и
J S l i d e r .VERTICAL), диапазон значений и начальное значение. Для поддержки со­
бытий с объектом J S l i d e r связывается обработчик C h a n g e L i s t e n e r . Из метода
s t a t e C h a n g e d обычно вызывается метод g e t V a l u e , позволяющий определить теку­
щее значение J S l i d e r .

Новые возможности: разметка шкалы


в составе Swing-компонента J S l i d e r могут отображаться основные и вспомога­
тельные метки шкалы. Для того чтобы включить отображение меток, надо вызвать
метод s e t P a i n t T i c k s ( t r u e ) . Расстояние между метками задается с помощью мето­
дов Ма j o r T i c k S p a c i n g и s e t M i n o r T i c k S p a c i n g . Если вы хотите ограничить выбор
пользователя только значениями, соответствующими меткам шкалы, вам надо вы­
звать метод s e t S n a p T o T i c k s ( t r u e ) . Включить отображение числовых значений
рядом с метками можно, вызвав метод s e t P a i n t L a b e l s ( t r u e ) . Вместо числовых
значений можно отображать любые компоненты (например, Image I c o n ) . Для этого
надо создать объект D i c t i o n a r y с ключами типа I n t e g e r и объектами Component в
качестве значений, соответствующих этим ключам. Созданный объект D i c t i o n a r y
следует связать с линейным регулятором с помощью метода s e t L a b e l T a b l e .
К прочим возможностям J S l i d e r относятся отображение рамок, изменение зна­
чений в обратном направлении, т.е. от старших к младшим ( s e t l n v e r t e d ( t r u e ) ) , и
способность распознавать перетаскивание, или перемещение мыши с нажатой кноп­
кой (в этом случае метод g e t V a l u e I s Ad j u s t i n g () возвращает значение t r u e ) , что­
бы отложить выполнение действия до окончания перетаскивания.

Конструкторы JSlider
в классе JSlider определено пять конструкторов:

public JSliderO
public JSlider(int orientation)
public JSlider(int m i n , int max)
public JSlider(int m i n , int max, int initialValue)
public JSlider(int orientation, int m i n , int max, int initialValue)
Первый из указанных конструкторов создает объект J S l i d e r с максимальным и
минимальным значениями, равными соответственно О и 100, и начальным значе­
нием, равным 50. Второй конструктор создает объект JSlider с теми же значения­
ми. От предыдущего конструктора он отличается тем, что при создании линейно­
го регулятора можно задавать его ориентацию (HORIZONTAL или VERTICAL). По­
следние т р и конструктора позволяют задавать произвольные минимальное,
максимальное и начальное значения.
14.7. Компонент JSIider 569

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


в классе J S I i d e r определены 43 метода. Одиннадцать из них, которые использу­
ются наиболее часто, описаны ниже. Каждому set-методу соответствует get-метод.

public void setMinimum(int min)


public void setMaximum(int max)
public void setValue(int initialValue)
public void setOrientation(int orientation)
Указанные методы позволяют устанавливать минимальное, максимальное и на­
чальное значения линейного регулятора, а также его ориентацию. Для определе­
ния ориентации используются константы HORIZONTAL и VERTICAL.

public void s e t P a i n t T i c k s ( b o o l e a n paint)


public void setMinorTickSpacing(int stepSize)
public void setMajorTickSpacing(int stepSize)
По умолчанию разметка шкалы линейного регулятора не отображается до тех пор,
пока не будет вызван метод s e t P a i n t T i c k s ( t r u e ) и установлено расстояние ме­
жду основными ( s e t M a j o r T i c k S p a c i n g ) или вспомогательными метками шкалы
(setMinorTickSpacing).
Методика профессионалов

Разметка шкалы линейного регулятора не будет отображаться до тех


пор, пока вы не установите расстояние между основными или вспомо­
гательными метками шкалы и не вызовете метод setPaintTicks с па­
раметром true.

public void s e t S n a p T o T i c k s ( b o o l e a n snap)


Вызвав метод s e t S n a p T o T i c k s ( t r u e ) , вы ограничите набор допустимых пози­
ций ползунка линейного регулятора метками шкалы. П о умолчанию установлено
значение соответствующего параметра, равное f a l s e ; в этом случае ползунок мо­
жет быть помещен в любой позиции.

public void s e t I n v e r t e d ( b o o l e a n inverted)


Метод s e t l n v e r t e d меняет местами минимальное и максимальное значения диа­
пазона линейного регулятора.

public void setPaintLabels(boolean paint)


public void setLabelTable(Dictionary labels)
Метод s e t P a i n t L a b e l s разрешает либо запрещает отображение значений рядом
с метками шкалы. В качестве значений могут использоваться объекты S t r i n g или
I c o n . Если метки шкалы не отображаются, значения также не будут выводиться.
Объект J S I i d e r может автоматически генерировать числовые значения, либо вы
570 Глава 1 4 . Основы Swing

можете организовать вывод рядом с метками шкалы произвольных объектов, ис­


пользуя метод s e t L a b e l T a b l e . В качестве параметра этому методу передается
объект Hashtable, в котором пара "ключ-значение" состоит из объектов I n t e g e r
и JComponent (обычно в роли JComponent выступает JLabel).
На з а м е т к у

Значения не будут отображаться до тех пор, пока не будут заданы


расстояния между основными или вспомогательными метками шка­
лы и вызван метод setPalntLahels (true).

В листинге 14.9 приведен пример приложения, в котором создаются три линей­


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

Листинг 14.9. J S l i d e r s . J a v a

import j a v a . a w t . * ;
import j a v a x . s w i n g . * ;
/** Простой пример, иллюстрирующий возможности J S l i d e r s ,
* в частности разметку шкалы.

p u b l i c c l a s s J S l i d e r s extends JFrame {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
new J S l i d e r s ( ) ;

public J S l i d e r s 0 {
super("Using J S l i d e r " ) ;
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
content.setBackground(Color.white);

JSlider sliderl = new JSlider();


sliderl.setBorder(BorderFactory.createTitledBorder
("JSlider without Tick Marks"));
content.add(sliderl, BorderLayout.NORTH);

JSlider slider2 = new JSlider();


slider2.setBorder(BorderFactory.createTitledBorder
("JSlider with Tick Marks"));
slider2.setMajorTickSpacing(20);
slider2.setMinorTickSpacing(5);
slider2.setPaintTicks(true);
content.add(slider2, BorderLayout.CENTER);

JSlider sliderS = new JSlider();


slider3.setBorder(BorderFactory.createTitledBorder
14.8. Компонент JColorChooser 571

("JSlider with Tick Marks & Labels'


sliders.setMajorTickSpacing(20);
sliders.setMinorTickSpacing(5);
slider3.setPaintTicks(true);
sliderS . setPaintLcQ^els (true) ;
content.add(sliders, BorderLayout.SOUTH);

packO ;
setVisible(true) ;

JSB«9rv^thdytTtekMart<$

gSiderwt&itlfcktilafks
JSIidJif ШХх Ней Ш ? k s
I»11) > I )}» f77TV77»*7)
JSHder with ШК Itafk? $, U N t s

I » » M ' ' » I • nT*»»77T7|


0 2Q 40 60 eC too о 20 4» m ш) ifm
(a) (6)
Рис. 14.10. Компонент JSlider позволяет выводить метки шкалы и отображать их
значения: а — интерфейс в стиле Windows; б — интерфейс в стиле Java (Metal);
в — интерфейс в стиле Motif

14.8. Компонент JColorChooser


Компонент J C o l o r C h o o s e r был впервые реализован в составе Swing. В AWT нет
аналога этому компоненту. J C o l o r C h o o s e r позволяет выбирать цвет в интерактив­
ном режиме. По умолчанию J C o l o r C h o o s e r отображает диалоговое окно со вклад­
ками, на которых пользователю предоставляется возможность выбрать образец цве­
та, определить цвет, задавая значения HSB (hue, saturation, brightness — цвет, насы­
щенность, яркость) или RGB.
Проще всего использовать J C o l o r C h o o s e r , вызывая метод J C o l o r C h o o s e r .
s h o w D i a l o g . В качестве первого параметра данному методу передается ссылка на ро­
дительский компонент, вторым параметром задается заголовок, а с помощью третье­
го параметра методу передается начальное значение цвета. Например:
JColorChooser.showDialog
(parent, " S e l e c t Background", getBackground());
В диалоговом окне отображаются кнопки OK, Cancel и Reset (рис. 14.11). Если
пользователь активизирует кнопку ОК, метод возвращает выбранное значение C o l o r .
Если пользователь заканчивает диалог нажатием клавиши <Esc> или щелчком на
кнопке Cancel, возвращается значение n u l l .
Объект J C o l o r C h o o s e r также можно создать с помощью конструктора. Этот под­
ход оправдан тогда, когда вам необходимо организовать выбор цвета за пределами
диалогового окна либо вы собираетесь многократно отображать окно выбора цвета.
В последнем случае надо передать экземпляр J C o l o r C h o o s e r методу J C o l o r C h o o s e r .
c r e a t e D i a l o g следующим образом:
572 Глава 14. Основы Swing

JColorChooser chooser = new JColorChooser();


JDialog d i a l o g = new JColorChooser.createDialog(
this, // Родительский компонент
" S e l e c t Color", // Заголовок
true, // Режим модального окна
chooser, // Средства выбора цвета
okListener, // Подтверждение выбора цвета
exitListener); // Отказ от выбора
Объекты, на которые ссылаются параметры o k L i s t e n e r и e x i t L i s t e n e r , долж­
ны реализовывать интерфейс A c t i o n L i s t e n e r . Эти объекты обрабатывают собы­
тия, связанные соответственно с выбором пользователем цвета и отменой выбора.
Вызов d i a l o g . s e t V i s i b l e ( t r u e ) отображает диалоговое окно, содержащее сред­
ства выбора цвета. Создавая объект JColorChooser и связывая его с JDialog, вы
добьетесь значительного увеличения производительности, поскольку метод show-
Dialog каждые! раз создает новый экземпляр JColorChooser.

Конструкторы
Наиболее часто используются следующие конструкторы класса: JColorChooser.

public JColorChooser()
public JColorChooser(Color initialColor)
Первый из указанных конструкторов создает JColorChooser с первоначально
выбранным значением C o l o r . w h i t e . Второй конструктор позволяет явно зада­
вать выбираемый цвет.

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


Ниже описаны шесть наиболее часто используемых методов класса JColorChooser.

public static Color shewDialog(Component parent, String title, Color initialColor)


Данный метод класса создает модальное диалоговое окно, содержащее новый объ­
ект JColorChooser. Параметры t i t l e и i n i t i a l C o l o r задают соответственно
заголовок и первоначально выбранный цвет. При отображении окна активной
становится вкладка с образцами цветов. Данный метод возвращает значение вы­
бранного цвета (объект Color) либо, если пользователь завершил работу с окном,
щелкнув на кнопке Cancel, значение n u l l .

public static JDialog createDialog(Component parent,


String title,
boolean modal,
JColorChooser chooser,
ActionListener okListener
ActionListener cancelListener)
Данный метод класса создает компонент JDialog, содержащий объект J c o l o r -
Chooser, переданный в качестве одного из параметров. Диалоговое окно может
14.8. Компонент JColorChooser 573

быть модальным или немодальным. При создании окна надо задать два объекта
A c t i o n L i s t e n e r , предназначенные для обработки событий, связанных с активи­
зацией пользователем кнопок ОК и Cancel. После создания диалогового окна надо
задать цвет посредством метода s e t C o l o r и отобразить окно, вызвав метод
s e t V i s i b l e . Например:
JColorChooser chooser = new JColorChooser();
JDialog d i a l o g = new J C o l o r C h o o s e r . c r e a t e D i a l o g ( . . . ) ;
chooser.setColor(someColor);
dialog.setVisible(true);
Обработчик o k L i s t e n e r может определить выбранный цвет посредством метода
getColor.
class okListener implements ActionListener {
public void actionPerformed(ActionEvent e)) {
Color color = chooser.getColorо;

repaint();
}
}

public Color getColorO


public void setColor(Color color)
public void setColor(int red, int green, int blue)
public void setColor(int color)
Первый из указанных методов возвращает выбранный цвет (Color), остальные
три метода устанавливают цвет JColorChooser. В методе s e t C o l o r ( i n t r e d ,
i n t g r e e n , i n t b l u e ) целочисленные параметры задают компоненты RGB,
которые должны находиться в диапазоне 0-255. В методе s e t C o l o r ( i n t c o l o r )
единственный параметр представляет собой байтовые значения цветов, оформ­
ленные в виде переменной i n t . Подробно способы кодирования цветов рассмот­
рены в описании класса Color.

В листинге 14.10 приведен пример простого компонента JFrame с кнопкой, при


активизации которой раскрывается окно JColorChooser. Выбранный цвет устанав­
ливается в качестве цвета фона для панели содержимого JFrame. Внешний вид пане­
ли образцов компонента JColorChooser показан на рис. 14.11.

Листинг 1 4 . 1 0 . J C o l o r C h o o s e r T e s t . J a v a

import j a v a . a w t . * ;
import J a v a . a w t . e v e n t . * ;
import j a v a x . s w i n g . * ;
/** Простой пример, иллюстрирующий использование JColorChooser. */
public c l a s s JColorChooserTest extends JFrame
implements A c t i o n L i s t e n e r {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
new J C o l o r C h o o s e r T e s t ( ) ;
574 Глава 14. Основы Swing

public JColorChooserTest () {
super("Using JColorChooser");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
content.setBackground(Color.white);
content.setLayout(new FlowLayout());
JButton colorButton
= new JButton("Choose Background Color");
colorButton.addActionListener(this);
content.add(colorButton);
setSize(300, 100);
setVisible(true);
}

public void actionPerformed(ActionEvent e) {


// В качестве параметров задаются родительский компонент,
// заголовок и начальный цвет.
Color bgColor
= JColorChooser.showDialog(this,
"Choose Background Color",
getBackgroundO ) ;
if (bgColor != null)
getContentPane().setBackground(bgColor);
}

^ C h o o s e Background Color

[Switches I И8В RGB

UJJJJJJJJ-
Recent:
шШ^шш. dddi
"-"11;
riiBlliH
D Sample Text Sample Text
D
OK I Cancel Reset
Рис. 14.11. Выбор цвета с помощью панели
образцов компонента JColorChooser
14.9. Внутренние окна 575

14.9. Внутренние окна


Многие коммерческие приложения, выполняющиеся в среде Windows, такие как
Microsoft PowerPoint, Corel Draw, Borland JBuilder и Allaire HomeSite, предоставляют
пользователю интерфейс, позволяющий работать с несколькими документами (MDI —
Multiple Document Interface). Это означает, что программа отображает одну большую
панель, выполняющую функции рабочего стола, в которой содержатся остальные ок­
на. Эти окна можно минимизировать, а также перемещать в пределах включающей
панели. При минимизации панели рабочего стола внутренние окна автоматически
исчезают из поля зрения пользователя.
Для поддержки MDI в Swing определены два класса. Первый из них, J d e s k t o p -
Рапе, поддерживает панель, которая включает остальные окна. Второй класс,
J I n t e r n a l F r a m e , действует подобно JFrame за исключением того, что объект дан­
ного класса может существовать только в пределах JDesktopPane. Использование
конструктора J I n t e r n a l F r a m e , которому передается только параметр, определяю­
щий заголовок, приводит к созданию внутреннего окна, не позволяющего изменять
размеры, закрывать, максимизировать и минимизировать окно. Однако в классе
J I n t e r n a l F r a m e также предусмотрен конструктор с пятью параметрами. С помо­
щью параметров конструктору передаются логические значения, определяющие каж­
дую из перечисленных выше характеристик (в указанном порядке). Внутренние окна
также содержат два метода, позволяющие управлять z-приоритетом: тоveToFront и
moveToBack.

Конструктор JInternalFrame
в классе J I n t e r n a l F r a m e определены шесть конструкторов, позволяющих опре­
делять поведение окна. Два самых простых конструктора, а также конструктор, по­
зволяющий задавать все характеристики объекта, описаны ниже.

public JInternalFrameO
public JInternalFrame(String title)
public JInternalFrame(String title,
boolean resizable,
boolean closeable,
boolean maximizable,
boolean iconifiable)
По умолчанию внутреннее окно не допускает изменения размеров, закрытия, мак­
симизации и минимизации. Окно, создаваемое с помощью первых дв)'х конструк­
торов, можно лишь перемещать в пределах включающей панели. Последний кон­
структор позволяет явно контролировать возможности окна.

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


Класс JInternalFrame содержит много методов, подобных методам JFrame.
Наиболее популярные из них описаны ниже.
576 Глава 1 4 . Основы Swing

public void setCloseable(boolean closeable)


public void setIconifiable(boolean iconifiable)
public void setMaximizable(boolean maximizable) public void
setResizable(boolean resizable)
Данные четыре метода позволяют контролировать поведение окна. Вместо get-
метода каждому из set-методов соответствует метод isXxx, с помощью которого
можно определять текущее значение соответствующего свойства. Каждый из set-
методов может генерировать исключение P r o p e r t y V e t o E x c e p t i o n .

public String getTitleO


public void setTitle(String title)
Метод g e t T i t l e позволяет определять текущий заголовок внутреннего окна, а
метод s e t T i t l e задает новый заголовок.

public void moveToBack()


public void moveToFrontO
Данные два метода управляют z-приоритетом внутреннего окна относительно дру­
гих внутренних окон, содержащихся во включающей панели.

public void show()


public void disposeO
Метод show отображает внутреннее окно. Если окно уже является видимым, оно пе­
ремещается на передний план. Перед отображением окна вы должны вызвать
setBounds (или s e t L o c a t i o n , или s e t S i z e ) , чтобы задать его расположение и
размеры. Причина в том, что с контейнером JLayeredPane, в котором содержатся
внутренние окна, не связан никакой диспетчер компоновки. Метод d i s p o s e запре­
щает отображение окна и может генерировать событие INTERNAL_FRAME_CLOSED.

М е т о д и к а профессионалов

Внутренние окна могут отображаться на панели рабочего стола


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

public void setFrameIcon(Icon image)


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

public void addInternalFrameListener(InternalFrameListener listener)


public void removeInternalFrameListener(InternalFrameListener listener)
Компонент J I n t e r n a l F r a m e s генерирует событие I n t e r n a l F r a m e E v e n t , анало­
гичное событию WindowEvent. В I n t e r n a l F r a m e L i s t e n e r объявлены семь аб­
страктных методов, которые предназначены для поддержки событий, связанных с
внутренним окном.
14.9. Внутренние окна 577

• internalFrameActivated
• internalFrameClosed
• internalFrameClosing
• internalFrameDeactivated
• internalFrameDeiconified
• internalFramelconified
• internalFrameOpened
Метод a d d l n t e r n a l F r a m e L i s t e n e r связывает обработчик с внутренним окном,
а метод r e m o v e I n t e r n a l F r a m e L i s t e n e r разрывает эту связь.
Класс JDesktopPane содержит два метода, позволяющих определять объекты
J I n t e r n a l F r a m e , содержащиеся во включающей панели. Метод g e t A l l F r a m e s воз­
вращает массив, содержащий все видимые и минимизированные внутренние окна.
Метод g e t A l l F r a m e s I n L a y e r ( l a y e r ) предоставляет сведения о всех внутренних
окнах в заданном слое JDesktopPane.
В примере, приведенном в листинге 14.11, пять пустых внутренних окон создают­
ся в панели, которая, в свою очередь, содержится в составе JFrame. Заметьте, что в
JDK 1.2 для J I n t e r n a l F r a m e значение характеристики, определяющей видимость,
устанавливается равным t r u e . Однако в JDK 1.3 значение этой характеристики по
умолчанию задается равным f a l s e , поэтому для отображения окна необходимо вы­
звать метод s e t V i s i b l e ( t r u e ) . На рис. 14.12,а все окна открыты, а на рис. 14.12,6
два внутренних окна минимизированы.
На з а м е т к у

В JDK 1,2 видимость внутреннего окна по умолчанию устанавливается


равной true, а в JDK 1.3 эта характеристика имеет значение false.

Листинг 14.11. JlntemalFrames. Java

import java.awt.*;
import Java.awt.event.*;
import javax.swing.*;
/** Простой пример, иллюстрирующий использование внутренних окон. */
public class JInternalFrames extends JFrame {
public static void main(String[] args) {
new JInternalFrames();

public JInternalFrames() {
super("Multiple Document Interface");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
content.setBackground(Color.white);
578 Глава 14. Основы Swing

JDesktopPane desktop = new JDesktopPane();


desktop.setBackground(Color.white)/
content.add(desktop, BorderLayout.CENTER);
setSize(450, 400);
for(int i=0; i<5; i++) {
JInternalFrame frame
= new JInternalFrame(("Internal Frame " + i ) ,
true, true, true, true);
frame.setLocation(i*50+10, i*50+10);
frame.setSize(200, 150);
frame.setBackground(Color.white);
frame.setVisible(true);
desktop.add(frame)/
frame.moveToFront();
}
setVisible(true);

^ M u l t i p l e Document Interfac

^шг^^^жйлмм

'§еашаг^^.£|Г^!^Ш!^д1тг<1
(а) (б)
Рис. 14.12. Swing-компонент JFrames содержит внутренние окна: а- все внутренние окна
открыты; б — два внутренних окна минимизированы

14.10. Компонент JOptionPane


Статические методы, определенные в классе J O p t i o n P a n e , дают возможность
легко создавать модальные диалоговые окна, позволяющие отображать сообщения
( J O p t i o n P a n e . s h o w M e s s a g e D i a l o g ) , запрашивать подтверждение ( J o p t i o n P a n e .
showConf i r m D i a l o g ) , вводить текст и выбирать опции ( J O p t i o n P a n e . Show-
I n p u t D i a l o g ) , а также активизировать одну из предложенных кнопок ( J o p t i o n ­
P a n e . s h o w O p t i o n D i a l o g ) . Эти методы возвращают значение i n t , указывающее, ка­
кая из кнопок была активизирована, либо значение S t r i n g , определяющее выбран­
ную опцию.
14.10. Компонент JOptionPane 579

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


в классе J O p t i o n P a n e определены 26 констант, 7 конструкторов и 59 методов.
Мы рассмотрим лишь пять статических методов, предназначенных для создания со­
общений и средств организации диалога. Во всех пяти методах три первых параметра
совпадают. С их помощью задается родительский компонент, сообщение, предназна­
ченное для отображения в диалоговом окне, и заголовок диалогового окна. В
табл. 14.1 перечислены константы, которые могут быть использованы в качестве па­
раметров методов J O p t i o n P a n e .

Таблица 1 4 . 1 . Константы JOptionPane

Тип пиктограммы в окне с сообщением PLAINMESSAGE


INFORMATION_MESSAGE
QUESTION_MESSAGE(no умолчанию)
WARNING_MESSAGE
ERROR_MESSAGE

Тип диалогового окна подтверждения DE FAULTOPTI ON


OK_CANCEL_OPTION
YES_NO_OPTION
YES_NO_CANCEL_OPTION (no умолчанию)

Значения, возвращаемые в результате YES_OPTION


подтверждения ^ 0 OPTION
CANCEL_OPTION
CLOSED_OPTION

public static v o i d s h o w M e s s s a g e D i a l o g ( C o m p o n e n t parent,


Object m e s s a g e ,
String title,
int iconType)
Данный метод создает простое модальное диалоговое окно, в котором отобража­
ются пиктограмма, сообщение и кнопка ОК. Поскольку данное окно предназначе­
но лишь для представления сообщения пользователю, метод s h o w M e s s s a g e -
D i a l o g не возвращает никакого значения. Вместе с сообщением в диалоговом ок­
не отображается пиктограмма, определяемая значением параметра i c o n T y p e .
Допустимые значения этого параметра приведены в табл. 14.1. П р и м е р ы диалого­
вых окон, создаваемых методом s h o w M e s s s a g e D i a l o g , показаны на рис. 14.13.
580 Глава 14. Основы Swing

1) INFORMATION UBSSf-^""- — O'JESUON^^MESSAGE коп

рок!
(а) (6)1
^ showMessageOiidoe М^^Ьсм!

W^J^N!NO„MeSSAGC л 6RR0P_WESSADEIC0R

ок1 ^ oiol
(в) (г)
Рис. 14.13. Диалоговые окна (интерфейс в стиле Windows) с
различными пиктограммами: а — информационное сообщение;
б — вопрос; в — предупреждение; г — сообщение об ошибке

Параметр PLAINMESSAGE соответствует окну без пиктограммы. Как правило, в


качестве значения параметра m e s s a g e задается объект S t r i n g . Однако вместо не­
го может быть использован любой объект ( O b j e c t ) . Объект J C o m p o n e n t отобра­
жается как обычный компонент, объект I c o n включается в состав компонента
J L a b e l , а любой другой объект перед выводом преобразуется с помощью метода
t o S t r i n g в объект S t r i n g .

public static int s h o w C o n f i r m D i a l o g ( C o m p o n e n t parent,


Object m e s s a g e ,
String title,
int o p t i o n T y p e ,
int i c o n T y p e )
Метод showConf i r m D i a l o g создает модальное диалоговое окно и возвращает от­
вет пользователя в виде целочисленного значения, равного одной из следующих
констант: YES_OPTION, NO_OPTION, CANCEL_OPTION или CLOSE_OPTION. Пара­
метр o p t i o n T y p e (допустимые типы опций перечислены в табл. 14.1) определяет,
какие кнопки должны отображаться. П р и м е р ы диалоговых окон показаны на
рис. 14.14.

жк'штч
DEFAULT_OPTЮN
и OK_CANCH-_OPTION

(а) (б)

ГДГ'1П?1!П1? шшшшшвсш
Д YESJtOJ)PJЮЫ
Д YES_NO_CANCEl._OPTION
I Yes : No Yes Cancel I
No
(в) (г)
Рис. 14.14. Диалоговые окна (интерфейс в стиле Java), созданные
с помощью метода showConfirmDialog: а— окно по умолчанию;
б — 0К/Сапсе1; в — Yes/No; г — Yes/No/Cancel
14.10. Компонент JOptionPane 581

Следующий фрагмент кода создает окно с подтверждением и обрабатывает ответ


пользователя.
int response = JOptionPane.showConfirmDialcg
(parentComponent,
"Do you l i k e J a v a ? " ,
"Confirm Dialog Example",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
i f ( r e s p o n s e == JOptionPane.YES_OPTION) {
/ / Действия в о т в е т на выбор кнопки Y e s .
} e l s e i f ( r e s p o n s e == JOptionPane.NO_OPTION) {
/ / Д е й с т в и я в о т в е т на выбор кнопки No.
} else {
/ / Прочие д е й с т в и я .
Параметр i c o n T y p e определяет пиктограмму, отображаемую рядом с сообщением.

p u b l i c static String s h o w I n p u t D i a l o g ( C o m p o n e n t parent,


Object m e s s a g e ,
String title,
int i c o n T y p e )
Метод S h o w I n p u t D i a l o g создает модальное диалоговое окно, содержащее пикто­
грамму, поле редактирования, предназначенное для ввода пользователем текста, а
также кнопки ОК и Cancel. Пример подобного окна показан на рис. 14.15. В окне все­
гда присутствуют кнопки ОК и Cancel. П р и активизации кнопки ОК метод возвраща­
ет объект S t r i n g , который содержит текст, введенный в поле редактирования. Если
работа с окном завершается после щелчка на кнопке Cancel, метод возвращает зна­
чение n u l l . Тип пиктограммы, отображаемой рядом с сообщением, определяется
параметром i c o n T y p e . Допустимые типы пиктограмм перечислены в табл. 14.1.

Рис. 14.15. Диалоговое окно (интерфейс в стиле


Motif), созданное методом s h o w I n p u t D i a l o g

public static Object s h o w I n p u t D i a l o g ( C o m p o n e n t parent,


Object m e s s a g e ,
String title,
int i c o n T y p e ,
Icon icon,
Object[] s e l e c t i o n s .
Object initialSelection)
Указанный вариант метода s h o w I n p u t D i a l o g предоставляет разработчику до­
полнительные возможности по определению характеристик окна. Параметр
s e l e c t i o n s определяет массив объектов, предоставляемых пользователю для вы-
582 Глава 14. Основы Swing

бора. Значения элементов массива представляются как объекты S t r i n g , поэтому в


объекте, помещаемом в массив, должен быть реализован метод t o S t r i n g . Если
массив содержит меньше 20 элементов, он представляется в виде раскрывающего­
ся списка (рис. 14.16), в противном случае для отображения массива используется
окно списка. Первоначально выбранный элемент массива задается с помощью па­
раметра i n i t i a l S e l e c t i o n . При активизации кнопки ОК возвращается значение
выбранного объекта, полученное в результате вызова метода t o S t r i n g . Если ра­
бота с окном завершается посредством кнопки Cancel, метод возвращает значение
n u l l . Тип пиктограммы, отображаемой рядом с сообщением, определяется пара­
метром iconType. Вместо стандартного изображения вы можете вывести в диало­
говом окне произвольную пиктограмму; она задается посредством параметра icon.

р Message to appear аЫме intM coirAo box


Serulefs

[ OK I ! CanceJ
Рис. 14.16. Диалоговое окно (интерфейс в стиле
Java) с раскрывающимся списком

public static Object showOptionDialog(Component parent,


Object message,
String title,
int iconType,
Icon image,
Object[] selections,
Object initialSelection)
Метод showOptionDialog позволяет использовать вместо традиционных кнопок
OK и Cancel произвольный набор кнопок (рис. 14.17). Как правило, массив
s e l e c t i o n s задает массив объектов JButton, а параметр i n i t i a l S e l e c t i o n
указывает, какая из кнопок должна получить фокус в начале работы с окном. Если в
массиве s e l e c t i o n s содержится объект S t r i n g , он автоматически преобразуется
в объект JButton. Например, следующий массив можно использовать как пара­
метр s e l e c t i o n s :
Object[] s e l e c t i o n s = { "OK,
"Cancel";
new JButton("End Program") };
В массив s e l e c t i o n s может быть включен любой объект JComponent. Однако
чаще всего метод showOptionDialog используется для создания окон, содержа­
щих кнопки. Пиктограмма, отображающаяся в окне, определяется параметром
iconType. При желании вы можете воспользоваться параметром i c o n для вывода
произвольной пиктограммы.

НЕ ТГ?Ш!1?1
Message to appear above ь ш о п options
•V
|8utton1 ii Bu11on2 1 Buttons | Рис. 14.17. Диалоговое окно (интерфейс в стиле
Windows), содержащее произвольный набор кнопок
1 4 . 1 1 . К о м п о н е н т JToolBar 583

Не следует недооценивать возможности по организации диалога, предоставляемые


методами s h o w O p t i o n D i a l o g и showConf i r m D i a l o g . Оба этих метода позволяют вы­
водить в качестве сообщения не только строку, но и произвольный объект. Например,
вы можете включить в контейнер J P a n e l набор флажков опций или переключателей
опций и передать J P a n e l в качестве параметра для вывода в диалоговом окне.
Все диалоговые окна, представленные в данном разделе, были созданы посредст­
вом программы J O p t i o n P a n e E x a m p l e s . J a v a . Исходный код программы находится
по адресу h t t p : //www. c o r e w e b p r o g r a m m i n g . com/.

14.11. Компонент JToolBar


Swing предоставляет разработчику новый компонент, не имеющий аналога в
AWT, — J T o o l B a r . Компонент J T o o l B a r реализует панель инструментов, выполняет
роль контейнера для кнопок небольшого размера и обеспечивает дополнительные
возможности по сравнению с J P a n e l . Основное отличие J T o o l B a r от других кон­
тейнеров состоит в том, что данный компонент является "плавающим", т.е. может
быть вынесен за пределы включающего окна и отображаться как независимое окно.
При необходимости J T o o l B a r можно снова перетащить мышью во включающее ок­
но. Несмотря на то что J T o o l B a r первоначально располагался в верхней части окна,
его можно "пришвартовать" сверху, снизу либо сбоку.
Чтобы создать компонент J T o o l B a r , надо вызвать пустой конструктор (для гори­
зонтально расположенной панели) либо передать конструктору параметр
J T o o l B a r . VERTICAL. О б ы ч н о горизонтальная панель располагается в области NORTH
или SOUTH контейнера, с которым связан диспетчер компоновки B o r d e r L a y o u t , а
вертикальная панель — в области EAST или WEST этого контейнера.
Методика профессионалов

Горизонтальная панель инструментов располагается в области


NORTH или SOUTH диспетчера компоновки BorderLayout, а вертикаль­
ная панель — в области EAST или WEST.

Единственная сложность, возникающая при использовании J T o o l B a r , связана с


содержащимися в нем кнопками. Вы можете либо включить в панели инструментов
обычный компонент J b u t t o n , либо добавить к панели объект A c t i o n (специальный
класс, реализующий A c t i o n L i s t e n e r и включающий информацию о пиктограммах и
надписях на кнопках), который автоматически создает J B u t t o n . В обоих случаях
проблема заключается в том, что графические кнопки, включаемые в панель инстру­
ментов, должны отличаться от обычных компонентов J B u t t o n .

1. Кнопка, отображаемая на панели инструментов, должна иметь небольшие раз­


меры, в то время как расстояние между содержимым и краями кнопки ддя
J B u t t o n относительно велико. Приходится вызывать метод s e t M a r g i n , зада­
вая нулевые или очень малые значения расстояний.
2. J B u t t o n помещает надпись справа от пиктограммы, но в панели инструментов
принято располагать надпись под изображениями. Чтобы разрешить эту про­
блему, надо вызвать методы s e t V e r t i c a l T e x t P o s i t i o n (BOTTOM) и s e t -
HorizontalTextPosition(CENTER).
584 Глава 14. Основы Swing

Пример "плавающего" компонента JToolBar показан на рис. 14.18.

Рис. 14.18. "Плавающий" компонент JToolBar, содержащий


еН1^111®|а|ш|| кнопки JButton с нулевыми значениями insets

Листинг 14.12.ToolBarButton.Java

import java.awt.*;
import javax.swing.*;

/** Простой пример, демонстрирующий применение JToolBar.


* Проблема в том, что если поместить JButton в JToolBar
* {или добавить объект Action), желаемый результат не
* будет достигнут. В панели инструментов должны присутствовать
* кнопки небольшого размера с пиктограммами и надписями под ними.
* По умолчанию надписи располагаются справа от пиктограмм.
* В JDK 1.3, если вы включите в панель инструментов объект
* Action, надпись не будет отображаться.
V
public class ToolBarButton extends JButton {
private static final Insets margins s=
new Insets(0, 0, 0, 0 ) ;

public ToolBarButton(Icon icon) {


super(icon);
setMargin(margins);
setVerticalTextPosition(BOTTOM);
setHorizontalTextPosition(CENTER);
}
public ToolBarButton(String imageFile) {
this(new Imagelcon(imageFile));
}

public ToolBarButton(String imageFile, String text) {


this(new Imagelcon(imageFile));
setText(text);
}
}

Конструкторы JToolBar
в классе JToolBar определены два конструктора:

public JToolBarO
public JToolBar(int orientation)
Первый конструктор создает панель инструментов JToolBar, ориентированную
по умолчанию горизонтально. Второй конструктор позволяет явно задать ориен­
тацию компонента: HORIZONTAL или VERTICAL.
1 4 . 1 1 . К о м п о н е н т JTooiBar 585

Методы JTooiBar
Класс JTooiBar содержит 23 метода. Ниже описаны лишь наиболее часто исполь­
зуемые из них.

public JButton add(Action action)


Данный метод создает кнопку, которая может поддерживать взаимодействие с
пользователем, а затем добавляет эту кнопку к панели инструментов. A c t i o n — это
расширение интерфейса A c t i o n L i s t e n e r (в котором объявлен метод a c t i o n -
Performed, предназначенной для поддержки событий ActionEvent). Обычно
методу add передается экземпляр подкласса класса A b s t r a c t A c t i o n (в котором
определены методы, объявленные в интерфейсе Action. A b s t r a c t A c t i o n созда­
ет объекты Icon и S t r i n g , предназначенные для хранения изображения и текста,
которые должны отображаться на кнопке. Использование класса A b s t r a c t -
A c t i o n иллюстрирует следующий пример:
public class MyApplet extends JApplet{
public void init{
JTooiBar toolbar = new JTooiBar();
toolbar.add(new PrintAction());
getContentPane().add(toolbar,
BorderLayout.WEST);

// Кнопка панели инструментов, поддерживающая событие


class PrintAction extends AbstractAction {
public PrintAction() {
super("Print",new Imagelcon("print.gif"));
}

public void actionPerformed(ActionEvent evt) {


System.out.println("Print button selected.");

}
}
Вы также можете связать с J B u t t o n обработчик A c t i o n L i s t e n e r и включить
кнопку в панель инструментов, вызывая метод add (someButton). Поскольку па­
нель инструментов является контейнером, ей доступны методы класса C o n t a i n e r .
В JDK 1.2 при добавлении к панели инструментов объекта A c t i o n на кнопке ото­
бражаются и пиктограмма, и текст. В JDK 1.3 в этом случае выводится только пик­
тограмма. Вместо вывода текста панель инструментов поддерживает подсказки.
На з а м е т к у ^^^^^

В JDK 1.2 при добавлении к панели инструментов объекта Action на «|fl|HL


кнопке отображаются и пиктограмма, и текст, в JDK 1.3— только щШ/^Ш
пиктограмма. ^|Д||^
586 Глава 14. Основы Swing

В документации по JDK 1.3 API рекомендуется не добавлять объект A c t i o n к пане­


ли инструментов, а связывать A c t i o n с кнопкой, а затем включать кнопку в ком­
понент JToolBar.
JToolBar t o o l b a r = new J T o o l B a r ( ) ;
JButton b u t t o n = new J B u t t o n O ;
button.setAction(new PrintAction());
toolbar.add(button);
В этом случае на кнопке будут отображаться и пиктограмма, и текст.

public void addSeparator()


public void addSeparator(Dimension size)
Данные методы предназначены для включения в состав панели инструментов раз­
делителей. Размеры разделителей по умолчанию определяются выбранным стилем
интерфейса. При вызове второго варианта метода a d d S e p a r a t o r ему передается
объект Dimension, в котором явно указываются ширина и высота пустого про­
странства.

public void setFloatable(boolean float)


public void setOrientation(int orientation)
Метод s e t F l o a t a b l e указывает, должна ли панель инструментов быть "плавающей", а
метод s e t O r i e n t a t i o n задает ориентацию панели инструментов. Обоим set-методам
соответствуют get-методы, позволяющие определить значения свойств.
В примере, код которого содержится в листингах 14.13 и 14.14, создается простой
компонент JFrame, в верхней части которого отображается панель инструментов
JToolBar. Данный объект JToolBar напоминает панель инструментов простого
Web-броузера. По умолчанию надписи на кнопках не отображаются, однако их можно
включить, установив флажок опции (компонент JCheckBox). С каждым объектом
J B u t t o n связано окно подсказки. На рис. 14.19 показана панель инструментов,
"пришвартованная" в горизонтальной и вертикальной позициях, а также в
"плавающем" режиме в виде отдельного окна. Кроме того, на рисунке показано со­
стояние окна, в котором на кнопках отображаются надписи.

Листинг 14.13.ToolBarExample.Java

import j a v a . a w t . * ;
import j a v a x . s w i n g . * ;
import J a v a . a w t . e v e n t . * ;
/** Небольшой пример, демонстрирующий использование JToolBar. */
p u b l i c c l a s s JToolBarExample extends JFrame
implements I t e m L i s t e n e r {
p r i v a t e BrowserToolBar toolbars-
p r i v a t e JCheckBox labelBox;
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
new JToolBarExample();
14.11. Компонент JToolBar 587

public JToolBarExample() {
super("JToolBar Example");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
content.setBackground(Color.white);

JPanel panel = new JPanel(new BorderLayout());


labelBox = new JCheckBox("Show Text Labels?");
labelBox.setHorizontalAlignment(SwingConstants.CENTER);
labelBox.addltemListener(this);
panel.add(new JTextArea(10,30), BorderLayout.CENTER);
panel.add(labelBox, BorderLayout.SOUTH);

toolbar = new BrowserToolBar();


content.add(toolbar, BorderLayout.NORTH);
content.add(panel, BorderLayout.CENTER);

packO ;
setVisible(true);
}
public void itemStateChanged(ItemEvent event) {
toolbar.setTextLabels(labelBox.isSelected());
packO ;
}
}

Листинг 14.14,BrowserToolBar.Java

import java.awt.'^;
import javax.swing.*;

/** Часть примера, демонстрирующего использование JToolBar.


* В данном примере создается плавающая панель инструментов,
* которая напоминает панель Web-броузера.
* При построении панели инструментов используется расширение
* класса JButton ToolBarButton. В компоненте, создаваемом с
* помощью объекта данного класса, уменьшены расстояния между
* содержимым кнопки и ее границами, а надпись располагается
* под пиктограммой.
V
public class BrowserToolBar extends JToolBar {
public BrowserToolBar() {
String[] imageFiles =
{ "Left.gif", "Right.gif", "RotCCUp.gif",
"TrafficRed.gif", "Home .gif", "Print.gif", "Help.gif"
String[] toolbarLabels =
{ "Back", "Forward", "Reload", "Stop",
"Home", "Print", "Help" };
588 Глава 14. Основы Swing

Insets margins = new Insets(0, 0, 0, 0 ) ;


for(int i=0; i<toolbarLabels.length; i++) {
ToolBarButton button =
new ToolBarButton("images/" + imageFiles[i] ;
button.setToolTipText(toolbarLabels[i]) ;
button.setMargin(margins);
add(button);
}
}
public void setTextLabels(boolean labelsAreEnabled) {
Component c;
int i = 0;
while((c = getComponentAtIndex(i++)) != null) {
ToolBarButton button = (ToolBarButton)c;
if (labelsAreEnabled) {
button.setText(button.getToolTipText())/
} else {
button.setText(null);
}
}
}

иптиш?
оИ1#И!&|а1Э1

Ш
г ghSjJyfTiwtLab^^

ШЛХоШаё Exampfe

# а т\
WlfH'fflit'' '^'^-'М1*'^^^^1аУх1 8вск1Forwaml
ней?!

КИ1сй>И|@Ш1ш|

Г" |гк>>5jexTub^s^ ' ishowText Labels'^

Рис. 14.19. Панель инструментов JToolBar: а— "пришвар­


тованная" горизонтально; б — "пришвартованная" вертикаль­
но; в — в "плавающем" режиме; г — на кнопках отображаются
надписи
14.12. Компонент JEditorPane 589

14.12. Компонент JEditorPane


Компонент J E d i t o r P a n e представляет собой разновидность текстовой области, в
которой может отображаться текст в различных форматах. Встроенная версия
J E d i t o r P a n e поддерживает только форматы HTML и RTF, но вы можете создать
собственный редактор для поддержки действий по редактированию, специфических
для конкретного приложения. Для выбора типа документа, предназначенного для
отображения, вызывается метод s e t C o n t e n t T y p e , а редактор, определенный разра­
ботчиком, задается с помощью метода s e t E d i t o r K i t . Если вы не создадите подкласс
J E d i t o r P a n e и не реализуете в нем поддержку требуемых данных, ваш выбор огра­
ничивается типами t e x t / h t m l , t e x t / r t f и t e x t / p l a i n . Если задан тип, не поддер­
живаемый компонентом, принимается значение t e x t / p l a i n .
На практике компонент J E d i t o r P a n e в основном используется для отображения
HTML-документов. Для работы с обычным текстом лучше применять J T e x t F i e l d .
Перечисленные ниже способы позволяют поместить данные в состав компонента
JEditorPane.
1. При создании объекта JEditorPane конструктору класса можно передать объект
URL или S t r i n g , определяющий URL документа (для Java-приложений URL
может указывать расположение файла на диске). Заметьте, что конструктор,
которому передается URL, может генерировать исключение lOException, по­
этому его надо помещать в блок t r y / c a t c h .
2. Построив объект J E d i t o r P a n e с помощью конструктора, вызываемого без па­
раметров, вы можете поместить в него данные с помощью метода s e t Page.
Данному методу также передается либо объект URL, либо объект S t r i n g , оп­
ределяющий расположение документа. Как и конструкторы J E d i t o r P a n e , ме­
тод s e t P a g e может генерировать исключение lOException. Данный подход в
основном используется тогда, когда содержимое области определяется в про­
цессе выполнения программы и выбор данных для редактирования зависит от
действий пользователя.
3. Для включения данных в компонент J E d i t o r P a n e можно использовать метод
s e t T e x t , передавая ему объект S t r i n g , который и является содержимым ком­
понента.
4. При необходимости вы можете использовать метод read, передавая ему объек­
ты I n p u t S t r e a m n HTMLDocument.
С помощью компонента J E d i t o r P a n e можно осуществлять редактирование тек­
ста, однако средства редактирования недостаточны для эффективной работы, поэто­
му J E d i t o r P a n e чаще применяется для просмотра текста. В этом случае перед выво­
дом данных в окно вызывается метод s e t E d i t a b l e ( f a l s e ) , запрещающий редакти­
рование. Как и для всех Swing-компонентов, при работе с s e t E d i t a b l e ( f a l s e )
можно реализовать прокрутку содержимого. Для этого надо поместить панель в со­
став объекта J E d i t o r P a n e .
String u r l = " h t t p : / / h o s t / p a t h / f i l e . h t m l " ;
try {
JEditorPane htmlPane = new J E d i t o r P a n e ( u r l ) ;
htmlPane.setEditable(false);
590 Глава 14. Основы Swing

someWindow.add(new JScrollPane(htmlPane);
} catch(lOException ioe) {
System.err.println("Error displaying " + url);
}

Переход no гипертекстовым ссылкам


Как было сказано выше, в большинстве случаев компонент J E d i t o r P a n e приме­
няется для отображения HTML-документов. П р и необходимости вы можете распо­
знавать действия пользователя по активизации гипертекстовых ссылок, выявлять, ка­
кая из ссылок была выбрана, и заменять содержимое J E d i t o r P a n e указанным доку­
ментом. Для обеспечения перехода по гипертекстовым ссылками с объектом связы­
вается обработчик H y p e r l i n k L i s t e n e r (заметьте, что его имя H y p e r l i n k L i s t e -
n e r , а не H y p e r L i n k L i s t e n e r ) , в котором реализован метод h y p e r l i n k U p d a t e . Для
связывания обработчика с объектом используется метод a d d H y p e r l i n k L i s t e n e r .
П р и получении события необходимо определить его тип с помощью g e t E v e n t Туре и
сравнить с H y p e r l i n k E v e n t . E v e n t T y p e .ACTIVATED. Последнее действие было не­
обязательно в ранних реализациях Swing, однако в Java 1.2, если вы не выполните его,
программа будет реагировать, даже если поместить курсор мыши на ссылку. П р и м е р
кода, предназначенного для поддержки гипертекстовых ссылок, приведен ниже.
public class SomeWindow extends JFrame
implements HyperlinkListener {
private JEditorPane htmlPane;

public void hyperlinkUpdate(HyperlinkEvent event) {


if (event.getEventType() ==
HyperlinkEvent.EventType.ACTIVATED) {
try {
htmlPane.setPage(event.getUIUi ()) ;
} catch(lOException ioe) {
// Предупреждающее сообщение для пользователя
}
}

Конструкторы JEditorPane
в классе J E d i t o r P a n e определены следующие четыре конструктора:

public JEditorPaneO
public JEditorPane(String url)
public J E d i t o r P a n e ( U R L url)
public JEditorPane(String m i m e T y p e , String d o c u m e n t )
По умолчанию J E d i t o r P a n e интерпретирует содержимое документа как обыч­
ный текст. П р и указании URL документа с помощью объекта URL или String авто-
14.12. Компонент JEditorPane 591

матически устанавливается HTML-редактор, и содержимое документа загружается


в окно J E d i t o r P a n e . Второй и третий конструкторы могут генерировать исклю­
чение lOException. Последний конструктор позволяет задавать М1МЕ-тип
( t e x t / p l a i n , t e x t / h t m l или t e x t / r t f ) и описание документа.

Методы JEditorPane
Ниже описаны часто используемые методы Knacca>JEditorPane. Каждому свойст­
ву Ххх соответствуют методы getXxx и setXxx.

public String getContentTypeO


public void setContentType(String mimeType)
Метод g e t C o n t e n t T y p e возвращает, a метод s e t C o n t e n t T y p e задает MIME-тип.
В настоящее время поддерживаются типы t e x t / p l a i n , t e x t / h t m l и t e x t / r t f .

public String getTextO


public void setText(String document)
Данные методы позволяют получать документ, содержащийся в J E d i t o r P a n e , и
устанавливать новый документ. Текст интерпретируется в соответствии с MIME-
типом, заданным для текущего редактора.

public void replaceSelection(String newText)


Метод r e p l a c e S e l e c t i o n заменяет выделенный фрагмент документа новым тек­
стом. В данном методе реализована многопотоковая поддержка (блокирование).
Если в качестве параметра задано значение n u l l , выделенный текст удаляется. Ес­
ли текст не выделен, содержимое объекта S t r i n g помещается в той позиции, на
которую указывает курсор. Замена текста допускается только в случае, если редак­
тирование документа разрешено. Для того чтобы разрешить редактирование, на­
до вызвать метод s e t E d i t a b l e ( t r u e ) .

public URL getPageO


public void setPage(String page)
public void setPage(URL url)
Метод g e t Page возвращает URL документа, отображаемого в составе компонента
J E d i t o r P a n e . Остальные два метода задают новый документ. Тип содержимого
определяется по URL и используется при регистрации соответствующего редак­
тора. В качестве параметра должен задаваться абсолютный URL. Оба метода могут
генерировать исключение lOException, поэтому их вызовы должны помещаться
в блок t r y / c a t c h .

public synchronized void addHyperlinkListener(HyperlinkListener listener)


public synchronized void removeHyperlinkListener(HyperlinkListener listener)
Метод a d d H y p e r l i n k L i s t e n e r связывает с объектом J E d i t o r P a n e обработчик
H y p e r l i n k L i s t e n e r , метод которого вызывается при активизации гипертексто­
вой ссылки. Метод r e m o v e H y p e r l i n k L i s t e n e r разрывает установленную связь.
592 Глава 14. Основы Swing

Если редактирование разрешено, переход по гипертекстовым ссылкам не выпол­


няется.

public void read(InputStream in, Object description)


Этот метод читает данные из входного потока в объект JEdi t o r Рапе. Если в опи­
сании документа указан тип HTMLDocument и зарегистрирован редактор
HTMLEditorKit, то данные из потока читаются этим редактором. В противном
случае для чтения потока используется суперкласс (JTextComponent) и данные
интерпретируются как обычный текст.

Реализация простого Web-броузера


Добавив к компоненту JEdi tor Рапе поле для ввода URL документа, можно реализо­
вать простой Web-броузер. В листингах 14.15 и 14.16 содержится код броузера, предна­
значенного для отображения НТМЬ-документов и обработки гипертекстовых ссылок.
Исходная страница расположена по адресу http://www. corewebprogramming.
com/. На рис. 14.20 показан внешний вид окна после загрузки в него документа
(http: //www. jhuapl. edu/rc/).

Листинг 14,15.Browser.Java

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
/•• Чрезвычайно простой "Web-броузер", созданный с
* использованием Swing. URL, заданный в командной строке,
* определяет документ, отображаемый при запуске броузера,
* и задает расположение исходной страницы.
V
public class Browser extends JFrame implements HyperlinkListener,
ActionListener {
public static void main(String[] args) {
if (args.length == 0)
new Browser("http://www.corewebprogramming.com/");
else
new Browser(args [0]);
}
private JIconButton homeButton;
private JTextField urlField;
private JEditorPane htmlPane;
private String initialURL;

public Browser(String initialURL) {


super("Simple Swing Browser");
this.initialURL = initialURL;
addWindowListener(new ExitListener()) ;
14.12. Компонент JEditorPane 593

WindowUtilities.setNativeLookAndFeel();
JPanel topPanel = new JPanelO;
topPanel.setBackground(Color.lightGray) ;
homeButton = new JIconButton("home.gif");
homeButton.addActionListener(this);
JLabel urlLabel = new JLabel("URL:");
urlField = new JTextField(30);
urlField.setText(initialURL) ;
urlField.addActionListener(this);
topPanel.add(homeButton);
topPanel.add(urlLabel);
topPanel.add(urlField);
getContentPane0.add(topPanel, BorderLayout.NORTH);
try {
htmlPane = new JEditorPane(initialURL);
htmlPane.setEditable(false);
htmlPane.addHyperlinkListener(this);
JScrollPane scrollPane » new JScrollPane(htmlPane);
getContentPane().add(scrollPane, BorderLayout.CENTER)
} catch(lOException ioe) {
warnUser("Can't build HTML pane for " + initialURL
+ ": " + ioe);
}
Dimension screenSize = getToolkit().getScreenSize();
int width = screenSize.width * 8 / 10;
int height = screenSize.height * 8 / 10;
setBounds(width/8, height/8, width, height);
setVisible(true);
}
public void actionPerformed(ActionEvent event) {
String url;
if (event.getSource () == urlField) {
url = urlField.getText0;
} else { // Clicked "home" button instead of entering URL.
url = initialURL;
}
try {
htmlPane.setPage(new URL(url));
urlField.setText(url);
} catch(lOException ioe) {
warnUser("Can't follow link to " + url + ": " + ioe);
}
}
public void hyperlinkUpdate(HyperlinkEvent event) {
if (event.getEventType 0 ==
HyperlinkEvent.EventType.ACTIVATED) {
try {
htmlPane.setPage(event.getURL());
urlField.setText(event.getURL().toExternalForm());
} catch(lOException ioe) {
warnUser("Can't follow link to "
+ event.getUIUiO.toExternalForm0 + ": " + ioe)
594 Глава 14. Основы Swing

private void warnUser(String message) {


JOptionPane.ShowMessageDialog(this, message, "Error",
JOptionPane.ERROR_MESSAGE);
}

Листинг 14.16.JIconButton.Java

import javax.swing.^;

/** Компонент JButton с Imagelcon */

public class JIconButton extends JButton


public JIconButton(String file) {
super(new Imagelcon(file));
setContentAreaFilled(false);
setBorderPainted(false);
setFocusPainted(false);
}
}

Simple Swing Browser

URL' jhttp/Awwwjhuapl ecJu/rc/

The Milion S. Eisenhower


Research & Technology
Development Center

СШмЭН APL scientists work at the frontiers of many research fields to support
technical program needs. Research Is performed in all of APL's technical
departments. However, the Milton S. Eisenhower Research and Technology
Development Center (RTDC) Is dedicated to basic and applied research in
support of APL's mission. In addition to nurturing scientifically based,
in-house expertise In newly developing areas, the RTDC provides a window
into science for APL. The RTDC contains numerous highly specialized
laboratories and conducts research programs in a number of broad areas.
Jj

Рис. 14.20. Простой броузер, созданный с помощью компо­


нентов Swing. JEditorPane обеспечивает поддержку HTML
14.13. Простые Swing-компоненты 595

Поддержка HTML и JavaHelp


в настоящее время J E d i t o r P a n e поддерживает лишь ограниченное подмножест­
во HTML 4.0. Многие языковые конструкции обрабатываются некорректно, и, хуже
того, некоторые HTML-выражения могут разрушить J E d i t o r P a n e , что приводит к
отображению длинной серии сообщений об ошибках. Поэтому выводить в приложе­
нии произвольные, не п р о в е р е н н ы е ранее HTML-документы, рискованно. По этой
причине чаще всего J E d i t o r P a n e применяется для отображения ограниченного, за­
ранее проверенного набора HTML-страниц, входящих в состав интерактивной спра­
вочной системы. П р и этом редактирование документов запрещено. Компания Sun
предоставляет небольшой пакет под названием JavaHelp, который облегчает создание
интерактивной справочной системы. Дополнительную информацию о JavaHelp мож­
но найти по а д р е с у h t t p : / / J a v a . s u n . c o m / p r o j e c t s / j a v a h e l p / .

14.13. Простые Swing-компоненты


Последнее, что осталось сделать в данной главе, — кратко рассмотреть простые
Swing-компоненты, для каждого из которых существует AWT-эквивалент: J C h e c k B o x ,
J R a d i o B u t t o n , J T e x t F i e l d и J T e x t A r e a . Кроме того, мы также обсудим компонент
J F i l e C h o o s e r , предоставляющий диалоговое окно для выбора файлов. Дополни­
тельную и н ф о р м а ц и ю по данному вопросу вы найдете в книге Кима Топли (Kim
Topley) CoreJava Foundation Classes.

Компонент JCheckBox
Swing-компонент JCheckBox (обратите внимание на прописную букву В в имени
JCheckBox) выполняет те же функции, что и AWT-компонент Checkbox. Для обработ­
ки событий используются объекты A c t i o n L i s t e n e r и I t e m L i s t e n e r . При работе с
A c t i o n L i s t e n e r приходится вызывать метод i s S e l e c t e d для того, чтобы отличить
установку флажка от сброса. При использовании I t e m L i s t e n e r объект I t e m E v e n t со­
держит информацию о компоненте; чтобы определить состояние JCheckBox, надо вы­
звать метод g e t S t a t e C h a n g e и сравнить возвращаемое значение с константами
I t e m E v e n t . SELECTED и I t e m E v e n t . DESELECTED. Если вы не можете гарантировать,
что цвет фона JCheckBox совпадает с цветом фона контейнера, вы должны вызвать ме­
тод s e t C o n t e n t A r e a F i l l e d ( f a l s e ) . Вместо квадратного поля, отображаемого по
умолчанию, вы можете задать произвольную пиктограмму (метод s e t l c o n ) . Сделав это,
не забудьте указать также пиктограмму, соответствующую установленному флажку оп­
ции ( s e t S e l e c t e d l c o n ) . В листинге 14.17 показан процесс создания флажков опций и
использования обработчиков I t e m L i s t e n e r и A c t i o n L i s t e n e r . В процессе обработ­
ки событий определяется состояние компонента. Результат выполнения программы
J C h e c k B o x T e s t . J a v a показан на рис. 14.21.
596 Глава 14. Основы Swing

Листинг 14.17. JCheclcBoxTest. Java

import javax.swing.*;
import java.awt.event.*;

public class JCheckBoxTest extends JPanel


implements ItemListener,
ActionListener{
JCheckBox checkBoxl, checkBox2;

public JCheckBoxTest0 {
checkBoxl = new JCheckBox("Java Servlets");
checkBox2 = new JCheckBox("JavaServer Pages");
checkBoxl.setContentAreaFilled(false);
checkBox2.setContentAreaFilled(false);
checkBoxl.addltemListener(this);
checkBox2.addActionListener(this);

add(checkBoxl);
add(checkBox2) ;
}

public void actionPerformed(ActionEvent event) {


System.out.println("JavaServer Pages selected; " +
checkBox2.isSelected() ) ;
}
public void itemStateChanged(ItemEvent event) {
JCheckBox checkbox = (JCheckBox)event.getltem();

if (event.getStateChange0 == ItemEvent.SELECTED) {
System, out .println (checkbox. getText () -f- " selected.");
} else {
System.out.println(checkbox.getText() + " deselected.");
}
}

public static void main(String[] args) {


JPanel panel = new JCheckBoxTest();
WindowUtilities.setNativeLookAndFeel();
WindowUtilities.openlnJFrame(panel, 300, 7 5 ) ;
}

Рис. 14.21. Стандартный компонент JCheckBox


14.13. Простые Swmg-компоненты 597

Компонент JRadioButton
Swing-компонент JRadioButton действует аналогично AWT-компоненту Checkbox,
включенному в состав CheckboxGroup. Для создания переключателей опций создается
несколько объектов JRadioButtons, которые объединяются с помощью ButtonGroup.
Все переключатели, помещенные в ButtonGroup, образуют одну логическую группу.
Как и при работе с JCheckBox, для поддержки событий можно использовать
A c t i o n L i s t e n e r и I t e m L i s t e n e r . Переключателю, выбранному в данный момент,
передаются события ActionEvent и ItemEvent, а переключателю, для которого вы­
бор был отменен, передается только событие ItemEvent. Если вы не можете гаранти­
ровать, что цвет фона JRadioButton совпадает с цветом фона контейнера, вы должны
вызвать метод s e t C o n t e n t A r e a F i l l e d ( f a l s e ) . Как и для JCheckBox, вы можете за­
дать пиктограмму, изменяющую внешний вид переключателя, принятый по умолчанию
(метод s e t Icon). В этом случае необходимо также задать пиктограмму, соответствую­
щую выбранной кнопке переключателя ( s e t S e l e c t e d l c o n ) . Три кнопки переключате­
лей опций, созданные в программе, приведенной в листинге 14.18, показаны на
рис. 14.22.

Листинг 14.18. JRadioButtonTest. Java

import j a v a x . s w i n g . J R a d i o B u t t o n ;
import javax.swing.ButtonGroup;
import j a v a . a w t . * ;
import J a v a . a w t . e v e n t . * ;
import j a v a x . s w i n g . * ;
p u b l i c c l a s s JRadioButtonTest extends JPanel
implements I t e m L i s t e n e r
public JRadioButtonTest0 {
S t r i n g [ ] l a b e l s = {"Java Swing","Java S e r v l e t s " ,
"JavaServer Pages"};
JRadioButton[] b u t t o n s = new J R a d i o B u t t o n [ 3 ] ;
ButtonGroup group = new ButtonGroup();
f o r ( i n t i=0; i < b u t t o n s . l e n g t h ; i++) {
b u t t o n s [ i ] = new J R a d i o B u t t o n ( l a b e l s [ i ] ) ;
buttons[i].setContentAreaFilled(false);
buttons[i].addltemListener(this);
group.add(buttons[i]);
add(buttons[i]);
}

public void itemStateChanged(ItemEvent event) {


JRadioButton radiobutton = (JRadioButton)event.getltem();

if (event.getStateChange0 == ItemEvent.SELECTED) {
System.out.println(radiobutton.getText() + " selected.");
} else {
System.out.println(radiobutton.getText() + " deselected.")
598 Глава 14. Основы Swing

public static void main(String[] args) {


JPanel panel = new JRadioButtonTest();
WindowUtilities.setNativeLookAndFeel();
WindowUtilities.openlnJFrame(panel, 400, 75);

г Java Swing ^ узуа SeryletS Г javaServer Pages

Рис. 14.22. Группа объектов J R a d i o B u t t o n

Компонент JTextField
Функции Swing-компонента J T e x t F i e l d почти полностью совпадают с функциями
AWT-компонента T e x t F i e I d . И тот и другой элемент позволяют задавать в конструкто­
ре начальный текст, ширину элемента и тип выравнивания. В отличие от T e x t F i e I d ,
после создания J T e x t F i e l d с помощью конструктора, вызываемого без параметров, вы
можете вызывать методы s e t T e x t , s e t C o l u m n s и s e t H o r i z o n t a l A l i g n m e n t . Типы
выравнивания задаются константами J T e x t F i e l d . LEFT, J T e x t F i e l d . CENTER и
J T e x t F i e l d . R I G H T . Для получения введенного текста используется метод g e t T e x t .
При нажатии клавиши <Enter> генерируется событие A c t i o n E v e n t , а при вводе обыч­
ных символов— событие D o c u m e n t E v e n t . За^метьте, что J T e x t F i e l d не применяется
для ввода пароля, для этой цели используется компонент J P a s s w o r d F i e l d .

Компонент JTextArea
Swing-компонент J T e x t A r e a похож на AWT-компонент T e x t A r e a и отличается от
него двумя особенностями. Во-первых, в J T e x t A r e a прокрутка по умолчанию не ис­
пользуется. Как и для всех Swing-компонентов, прокрутка реализуется с помощью
J S c r o l l P a n e . Во-вторых, текстовые области реализуются с помощью J T e x t A r e a и
J E d i t o r P a n e и позволяют не только выводить текст, но и выполнять более сложные
действия. В частности, J E d i t o r P a n e дает возможность отображать HTML- и RTF-
документы.

Компонент JFileChooser
Управляющий элемент J F i l e C h o o s e r (рис. 14.23) позволяет пользователям вы­
бирать файлы в интерактивном режиме, просматривая содержимое каталогов.
Обычно при создании экземпляра J F i l e C h o o s e r конструктору класса передается
объект S t r i n g , определяющий каталог (строка "." соответствует тек)^щему каталогу).
Вы также можете указать файл, выбираемый по умолчанию (метод s e t S e l e c t e d -
F i l e ) , и ограничить список отображаемых файлов указанными типами (для этого на­
до создать объект F i l e F i l t e r и связать его с окном выбора файла). Для определения
14.13. Простые Swing-компоненты 599

заголовка окна служит метод s e t D i a l o g T i t l e . Чтобы отобразить окно, надо вызвать


s h o w O p e n D i a l o g или s h o w S a v e D i a l o g , передавая в качестве параметра родитель­
ское окно. В результате выполнения метода, отображающего окно выбора файла,
возвращается целочисленное значение. Если возвращенное значение равно
J F i l e C h o o s e r . APPROVEOPTION, это значит, что пользователь выбрал файл и не
воспользовался кнопкой Cancel. Для того чтобы определить выбранный файл, надо
вызвать метод g e t S e l e c t e d F i l e . Например:
JFileChooser chooser = new JFileChooser(".");
int result = chooser.ShowOpenDialog(parent);
File file = chooser.getSelectedFile();
if (file != null &&
result == JFileChooser.APPROVE_OPTION) {
// Выполнить требуемые действия
}
Вызвав перед открытием окна метод s e t M u l t i S e l e c t i o n E n a b l e d ( t r u e ) , вы
разрешите пользователю выбирать несколько файлов. Метод g e t S e l e c t e d F i l e s
помещает выбранные файлы в массив F i l e .

[ЗЗЗНННВВННН
LOOK in \....> >

!J...J Allaire
" ^ MMi
L J BACKUP
"3
1, JBDK1.1
1 .iCDROM

, ж111..^л.1.и-СШ^И1^^И
••••i-J Рис. 14.23. Компонент J F i l e C h o o s e r
Ftlenantic; |t"-WP 2na-Edition
позволяет пользователю выбирать
Fites of type \>\\\ Fnes г "i Canc^et 1 в интерактивном режиме один или
d.
несколько файлов

С любым компонентом JComponent можно связать объект B o r d e r , определенный


разработчиком. Для этого надо вызвать метод s e t B o r d e r . Вы также можете задать
отображение окна подсказки, которое будет выводиться в тех случаях, когда курсор
мыши задержится на определенное время на компоненте. Д,ля этого применяется ме­
тод s e t T o o l T i p T e x t .

14-14. Резюме
в данной главе обсуждался ряд Swing-компоненов, имеющих аналоги в пакете
AWT: J L a b e l , J B u t t o n , J S l i d e r , J P a n e l , J A p p l e t и J F r a m e . Кроме того, здесь бы­
ли рассмотрены компоненты, специфические для Swing, такие как J C o l o r C h o o s e r ,
J O p t i o n P a n e и J I n t e r n a l F r a m e . Все Swing-компоненты, за исключением окон, яв­
ляются легковесными и отображаются с помощью Java-кода. Панель содержимого в
J F r a m e и J A p p l e t представляет собой объект J P a n e l , реализующий по умолчанию
двойную буферизацию. И с J F r a m e и с J A p p l e t связан диспетчер компоновки
BorderLayout.
Swing-компоненты предоставляют разработчику богатые возможности. В частно­
сти, при слздании интерфейса можно выбирать его стиль, создавать новое обрамле-
600 Глава 14. Основы Swing

ние и определять окна подсказки. На кнопках может отображаться не только текст,


но и пиктограммы. При создании надписей можно использовать HTML-код. Линей­
ные регуляторы поддерживают изменение значений в прямом и обратном направле­
ниях; рядом с метками шкалы могут отображаться текстовые значения или пикто­
граммы. Модальное окно JOpt ion Рапе предоставляет пользователю практически
любые компоненты для выбора, а также позволяет вводить информацию, задавая
текст, выбирая пункты раскрывающегося списка и активизируя кнопки. Компонент
JColorChooser предоставляет интерактивные средства для выбора цвета.
В данной главе мы лишь кратко рассмотрели простые средства Swing API. Сле­
дующая глава посвящена обсуждению более сложных компонентов J L i s t , JTree и
JTable. При работе с данными компонентами могут возникать определенные труд­
ности, поскольку они созданы на основе моделей данных. Однако, один раз освоив
эти средства, вы сможете создавать интерфейсы для своих Java-программ на профес­
сиональном уровне.
РАСШИРЕННЫЕ
СРЕДСТВА SWING

В ЭТОЙ главе...

• Использование моделей данных и средств визуализации.

• Включение пунктов списка компонента J L i s t и их


удаление.
• Отображение компонентов, определяемых пользователем
в составе J L i s t .

• Динамическое отображение JTree.

• Пиктограммы, определенные пользователем


в составе J T r e e .

• Создание компонента JTable с возможностью прокрутки.

• Редактирование ячеек JTable.

• Вывод Swing-компонентов на печать.

• Обновление Swing-компонентов в многопотоковом режиме.


J^y\zJsJ:£J

В
этой главе мы рассмотрим т р и компонента, которые используются для создания
сложных графических интерфейсов: J L i s t , J T r e e и J T a b l e . Списки Swing по­
хожи на знакомые вам AWT-компоненты L i s t , однако, помимо вывода набора
строк, компонент J L i s t позволяет также отображать пиктограммы. Компоненты
J T r e e и J T a b l e н е имеют AWT-аналогов. J T r e e представляет данные в виде иерар­
хической древовидной структуры. Примером древовидной структуры может служить
иерархия каталогов и файлов, отображаемая многими приложениями. Компонент
J T a b l e позволяет представить данные в виде двухмерной таблицы. Все т р и компо­
нента соответствуют архитектуре *'модель-просмотр-контроллер", и данные для ка­
ждого из них хранятся в модели данных. П р и изменении данных генерируется собы­
тие, сообщающее о том, что представление данных необходимо обновить.
Все т р и компонента предоставляют модели данных со встроенными методами,
предназначенными для модификации данных и генерации событий. В соответствии с
моделями данных и н ф о р м а ц и я представляется как объекты J L a b e l . Мы покажем
вам, как создавать собственные модели данных и средства просмотра, позволяющие
представлять в составе рассматриваемых компонентов другие объекты. Определяя
собственные средства просмотра, вы можете отображать в рассматриваемых компо­
нентах любые простые элементы Swing, например выводить изображения в составе
древовидной структуры либо флажки опций в ячейках таблицы.
В данной главе мы также рассмотрим два важных вопроса: вывод Swing-
компонентов на печать и обновление этих компонентов с учетом многопотоковой
обработки. Класс G r a p h i c s 2D, рассмотренный в главе 10, позволяет масштабировать
компоненты для вывода высококачественной графики на п р и н т е р . Мы покажем вам,
как открывать диалоговое окно принтера, предоставляющее пользователю возмож­
ность выбора опций, и создавать методы для вывода на печать Swing-компонентов.
По сравнению с AWT процедура вывода на печать Swing-компонентов претерпела
существенные изменения. Со Swing-компонентами связываются независимые обрам­
ления и UI-представители, предназначенные ддя создания и н т е р ф е й с о в выбранного
стиля. Изменение состояния Swing-компонентов всегда производится в потоке, вы­
полняющем передачу событий. В данной главе мы рассмотрим порядок обращения к
604 Глава 15. Расширенные средства Swing

Swing-компонентам из потоков, определяемых пользователем. Мы также продемон­


стрируем обновление компонента из метода r u n объекта R u n n a b l e и помещение
объектов в очередь событий для обработки в соответствующем потоке.
Вопросы, рассматриваемые в данной главе, предполагают использование плат­
ф о р м ы Java 2. Если вы собираетесь включить описанные здесь компоненты в состав
аплета, вам надо либо обеспечить доставку Swing-классов в JAR-файле, либо инстал­
лировать на машине клиента продукт Java Plug-In.

1 5 . 1 . Использование произвольных моделей


данных и средств просмотра
Практически все Swing-компоненты позволяют разделять структуру данных и
средства их воспроизведения. Часто для хранения данных используются массивы; та­
кой подход применяется, в частности, при работе со списками, деревьями и таблица­
ми. Swing также позволяет определять для и н т е р ф е й с н ы х компонентов новые струк­
туры данных. П р и этом необходимо реализовать и н т е р ф е й с , сообщающий Swing-
компоненту порядок доступа к данным и их воспроизведения. Преимущество такого
подхода состоит в том, что и н ф о р м а ц и я не копируется в компонент; вы лишь описы­
ваете способы обращения к ней.
При отображении данных с помощью компонентов J L i s t , J T r e e и J T a b l e дейст­
вует ряд соглашений. Н а п р и м е р , в компоненте J L i s t значения типа S t r i n g или
I c o n выводятся непосредственно, а другие объекты преобразуются к строковому виду
посредством метода t o S t r i n g , а затем отображаются как J L a b e l . Кроме того. Swing
позволяет устанавливать правила отображения конкретных данных в компоненты
так, что каждый элемент J L i s t может выводиться как отдельный компонент. Для то­
го чтобы обеспечить отображение произвольных данных, содержащихся в списке,
необходимо создать средства "визуализации ячеек", которым передаются ссылки на
J L i s t , значения и и н ф о р м а ц и я о состоянии (например, сведения о том, выбран ли
данный пункт). На основании этих данных средство визуализации ячеек выбирает
объект J C o m p o n e n t для отображения.
В следующем разделе мы рассмотрим различные подходы, связанные с использова­
нием моделей и средств представления для отображения данных J L i s t . Модели и сред­
ства представления для J T r e e и J T a b l e будут рассмотрены в последующих разделах.

15.2. Компонент JList


Компонент J L i s t представляет список, в котором пользователь может выбрать
один или несколько пунктов. Н и ж е мы опишем четыре подхода к построению J L i s t .
Первый подход состоит в создании фиксированного набора пунктов; при этом дан­
ные непосредственно передаются J L i s t . Второй подход использует модель по умол­
чанию, поддерживающую изменяющийся набор пунктов. Согласно третьему подходу,
для поддержки пунктов списка применяется модель данных, определяемая пользова­
телем. И, наконец, четвертый подход позволяет использовать средства представле­
ния, определяемые разработчиком, которые создают для каждого пункта списка от­
дельный объект J C o m p o n e n t .
15.2. Компонент JList 605

JList как фиксированный набор пунктов


Проще всего создать компонент J L i s t , передав массив строк конструктору данно­
го класса. В отличие от AWT-компонента L i s t , J L i s t не позволяет добавлять эле­
менты в готовый список или удалять их. Для создания динамического списка необхо­
димо использовать объект ListModel, который будет описан далее в этом разделе.
Однако очень часто фиксированный набор пунктов вполне удовлетворяет требова­
ниям конкретного приложения; в таком случае разработчик может использовать для
создания списка код, подобный приведенному ниже.
S t r i n g [ ] o p t i o n s = { "Option 1", . . . , "Option N" };
J L i s t o p t i o n L i s t = new J L i s t ( o p t i o n s ) ;
Число отображаемых строк устанавливается с помощью метода s e t V i s i b l e Row-
Count. Если число пунктов списка превышает число видимых строк, приходится реа-
лизовывать в составе компонента J L i s t полосы прокрутки. Как и для всех элементов
Swing, для поддержки прокрутки надо поместить компонент в панель J S c r o l l P a n e .
optionList.setVisibleRowCount(4);
JScrollPane optionPane = new JScrollPane(optionList);
someContainer.add(optionPane);
Компонент J L i s t генерирует события L i s t S e l e c t i o n E v e n t , для их поддержки
надо связать с данным компонентом обработчик L i s t S e l e c t i o n L i s t e n e r , в кото­
ром реализован метод valueChanged. Один щелчок мыши генерирует три события,
связанных со списком. Одно из них соответствует отмене выбора пункта, который
был выбран раньше, второе — перемещению выбора, а третье — выбору нового пунк­
та. В первых двух случаях метод g e t V a l u e l s A d j u s t i n g объекта L i s t S e l e c t i o n ­
Event возвращает значение true, поэтому если вас интересует только выбор нового
пункта, вам надо выполнять реальные действия по обработке только в случае, если
данный метод возвращает значение f a l s e . При разработке программы вы можете
вовсе отказаться от обработки событий и выяснять, какой из пунктов списка был вы­
бран последним, вызывая метод g e t S e l e c t e d V a l u e либо g e t S e l e c t e d l n d e x . Ес­
ли компонент JList должен поддерживать выбор нескольких пунктов, вы можете за­
дать требуемый режим с помощью метода s e t S e l e c t i o n M o d e . При вызове данного
метода ему передается одна из следующих констант: SINGLESELECTION,
SINGLE_INTERVAL_SELECTION и MULTIPLE_INTERVAL_SELECTION. Для получения
массива выбранных пунктов используется метод g e t S e l e c t e d V a l u e s либо
g e t S e l e c t e d l n d i c e s . Пример создания компонента J L i s t приведен ниже.
p u b l i c c l a s s SomeClass {
private JList optionList;
p u b l i c void someMethod() {
MyListListener l i s t e n e r = new M y L i s t L i s t e n e r ( ) ;
optionList.addListSelectionListener(listener);
}

p r i v a t e c l a s s MyListListener
implements L i s t S e l e c t i o n L i s t e n e r {
p u b l i c void v a l u e C h a n g e d ( L i s t S e l e c t i o n E v e n t event) {
606 Глава 15. Расширенные средства Swing

// Обрабатывается только выбор нового пункта.


// Метод getValuelsAdjusting должен возвращать
// значение false,
if (!event.getValuelsAdjustingО) {
String selection =
optionList.getSelectedValue{);
doSomethingWith(selection);
}

}
В случае применения обработчика в теле метода, вызываемого при появлении со­
бытия, следует вызвать метод g e t V a l u e l s A d j u s t i n g класса L i s t S e l e c t i o n E v e n t и
проверить, возвращает ли этот метод значение f a l s e . В данном случае два события,
сообщающих о том, что выбор предыдущего пункта был отменен, игнорируются.
Методика профессионалов

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


ListSelectionEvent. Если вас интересуют только сведения о вновь вы­
бранном пункте, проверьте, возвращает ли метод getValuelsAdjusting
значение false.

Конструкторы класса JList


в классе J L i s t определено четыре конструктора:

p u b l i c JList()
p u b l i c JList(Object[] data)
p u b l i c JList(Vector data)
p u b l i c JList(ListModel m o d e l )
Первый конструктор создает пустой объект J L i s t , не содержащий информации.
Второй и третий конструкторы принимают данные, о ф о р м л е н н ы е соответствен­
но в виде массива и объекта V e c t o r . П р и вызове четвертого конструктора ему пе­
редается объект L i s t M o d e l , который содержит методы, позволяющие определять
размер списка и получать значения пунктов.

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


в классе J L i s t определено около 60 методов. Тринадцать из них, которые ис­
пользуются наиболее часто, описаны ниже.

public void clearSelection()


Данный метод отменяет выбор всех пунктов списка.

p u b l i c L i s t M o d e l getModel()
p u b l i c void s e t M o d e l ( L i s t M o d e l m o d e l )
Метод g e t M o d e l позволяет получить модель данных, содержащую пункты списка,
предназначенные для отображения. Метод s e t M o d e l задает модель данных.
15.2. Компонент JList 607

public int getSelectedIndex()


public int[] getSelectedlndicesO
Метод g e t S e l e c t e d l n d e x возвращает индекс первого из выбранных пунктов
списка. Метод g e t S e l e c t e d l n d i c e s возвращает целочисленный массив, в кото­
ром содержатся индексы всех выбранных пунктов.

public Object getSelectedValue()


public Object[] getSelectedValuesO
Метод g e t S e l e c t e d V a l u e возвращает первый из выбранных пунктов списка. Ме­
тод g e t S e l e c t e d V a l u e s возвращает массив Object, в котором содержатся все
выбранные пункты.

public int getSelectionModeO


public setSelectionMode(int mode)
Метод g e t S e l e c t i o n M o d e возвращает, a метод s e t S e l e c t i o n M o d e устанавлива­
ет режим выбора пунктов списка. Режим задается с помощью следующих констант:
ListSelectionModel.SINGLE_SELECTION,
ListSelectionModel.SINGLE_INTERVAL_SELECTION и
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION.

public boolean getValuelsAdjustingO


Событие L i s t S e l e c t i o n E v e n t генерируется в трех случаях: при отмене выбора
текущего пункта списка, при перемещении выбора (перетаскивании курсора мы­
ши) и при выборе нового пункта (окончание выбора соответствует отпусканию
кнопки мыши). В первых двух случаях метод g e t V a l u e l s A d j u s t i n g возвращает
значение t r u e , а в третьем случае (когда пункт списка выбран, а курсор мыши от­
пущен) — значение f a l s e .

public int getVisibleRowCountO


public void setVisibleRowCount(int rows)
Метод getVisibleRowCount возвращает, a метод setVisibleRowCount задает
число видимых строк списка.

public boolean isSelectedIndex(int index)


Данный метод возвращает t r u e , если пункт списка с указанным индексом выбран.
В противном случае возвращается значение f a l s e .
В листинге 15.1 показан код приложения, которое создает простой объект J L i s t ,
содержащий массив строк, и помещает его в панель с поддержкой прокрутки. Обработ­
чик, связанный с компонентом J L i s t , отображает выбранный пункт в поле редактиро­
вания (рис. 15.1). Коды вспомогательных классов W i n d o w U t i l i t i e s и E x i t L i s t e n e r
были описаны в начале главы 14. Код приложения, как и все другие коды, приведенные
в данной книге, доступен по адресу h t t p : //www. corewebprograrraning.com/.
608 Глава 15. Расширенные средства Swing

Листинг 15.1. JListSimpleExample. Java

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;

/** Simple JList example illustrating


* <UL>
* <LI>Coздaниe компонента JList; компоненты передаются
* непосредственно конструктору JList
* <LI>Для выявления изменений используется обработчик событий.
* </UL>

public class JListSimpleExample extends JFrame {


public static void main(String[] args) {
new JListSimpleExample0;
}
private JList sampleJList;
private JTextField valueField;

public JListSimpleExample() {
super("Creating a Simple JList");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();

// Создание JList, установка числа видимых строк,


// добавление обработчика и помещение компонента
// в состав JScrollPane.
String[] entries = { "Entry 1", "Entry 2", "Entry 3",
"Entry 4", "Entry 5", "Entry 6" };
sampleJList = new JList(entries);
sampleJList.setVisibleRowCount(4);
sampleJList.addListSelectionListener(new ValueReporter());
JScrollPane listPane = new JScrollPane(sampleJList);
Font displayFont = new Font("Serif", Font.BOLD, 18);
sampleJList.setFont(displayFont);

JPanel listPanel = new JPanelO;


listPanel.setBackground(Color.white);
Border listPanelBorder =
BorderFactory.createTitledBorder("Sample JList");
listPanel.setBorder(listPanelBorder);
listPanel.add(listPane);
content.add(listPanel, BorderLayout.CENTER);
JLabel valueLabel = new JLabel("Last Selection:");
valueLabel.setFont(displayFont);
valueField = new JTextField("None", 7 ) ;
valueField.setFont(displayFont);
valueField.setEditable(false) ;
JPanel valuePanel = new JPanelO;
valuePanel.setBackground(Color.white);
15.2. Компонент JList 609

Border valuePanelBorder =
BorderFactory.createTitledBorder("JList Selection")
valuePanel.setBorder(valuePanelBorder);
valuePanel.add(valueLabel) ;
valuePanel.add(valueField) ;
content.add(valuePanel, BorderLayout.SOUTH);
packO ;
setVisible(true);

private class ValueReporter implements ListSelectionListener {

/** В результате действий пользователя генерируются три


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

public void valueChanged(ListSelectionEvent event) {


if (!event.getValuelsAdjusting()) {
Object value = sampleJList.getSelectedValue();
if (value != null) {
valueField.setText(value.toString());
}
}

ШШШШШШШШ
Sample JList

Entry 1 '
Entry!

Entry 4jrj
JList Selection

Last Selection: JEntry 5


Рис. 15.1. Компонент JList с фиксированным набором пунктов

Компонент JList с изменяемым набором пунктов


Для того чтобы создать компонент J L i s t , надо сначала создать объект
DefaultListModel, используя конструктор по умолчанию. Класс D e f a u l t L i s t M o d e l
содержит те же методы, что и класс j a v a . u t i l . Vector, поэтому вы можете выполнять
с данными те же действия, что и при работе с соответствующим объектом. Создав мо­
дель списка, ее надо передать конструктору J L i s t . После этого все изменения объекта
DefaultListModel будут отражаться в компоненте J L i s t .
610 Глава 15. Расширенные средства Swing

В процессе работы программы можно добавлять пункты к компоненту J L i s t и


удалять их. Для этого используются те же методы, что и при работе с объектом
V e c t o r . Например:
S t r i n g c h o i c e s = { "Choice 1", . . . , "Choice N " } ;
D e f a u l t L i s t M o d e l sampleModel = new D e f a u l t L i s t M o d e l ( ) ;
f o r ( i n t i = 0 ; i < c h o i c e s . l e n g t h ; i++) {
sampleModel.addElement(choices[i]);
}
J L i s t o p t i o n L i s t = new J L i s t ( s a m p l e M o d e l ) ;
Метод a d d E l e m e n t добавляет пункт к концу списка, а метод r e m o v e ( i n d e x ) уда­
ляет из списка пункт с указанным индексом. Добавление и удаление пунктов списка
могут привести к изменению размеров компонента, поэтому для обновления внешне­
го вида окна, содержащего J L i s t , следует вызвать методы r e v a l i d a t e и v a l i d a t e .
В листинге 15.2 вместо строкового массива конструктору J L i s t передается объект
D e f a u l t L i s t M o d e l , в который включен ряд пунктов. В окно приложения мы поме­
щаем компонент J B u t t o n , с которым связан обработчик A c t i o n L i s t e n e r , реализо­
ванный как класс I t e m A d d e r . По щелчку на кнопке метод a c t i o n P e r f o r m e d обра­
ботчика добавляет новый пункт к модели данных списка, а затем вызывает метод
r e v a l i d a t e панели, содержащей J L i s t . Результаты выполнения программы показа­
ны на рис. 15.2.

Листинг 1 5 . 2 . D e f a u l t L i s t M o d e l E x a m p l e . j a v a

import java.awt.*;
import Java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

/** Пример и с п о л ь з о в а н и я J L i s t , иллюстрирующий


* <UL>
* <LI>Coздaниe JList путем построения DefaultListModel,
* добавления значений и передачи данного объекта
* конструктору JList.
* <LI>Дoбaвлeниe новых значений в процессе выполнения
* программы (основная возможность, реализуемая DefaultListModel)
* </UL>

public class DefaultListModelExample extends JFrame {


public static void main(String[] args) {
new DefaultListModelExample0;
}
JList sampleJList;
private DefaultListModel sampleModel;

public DefaultListModelExample() {
super("Creating a Simple JList");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
15.2. Компонент JList 611

string[] entries = { "Entry 1", "Entry 2", "Entry 3",


"Entry 4", "Entry 5", "Entry 6" };
sampleModel = new DefaultListModel();
for(int i=0; i<entries.length; i++) {
sampleModel.addElement(entries[i]);
}
sampleJList = new JList(sampleModel);
sampleJList.setVisibleRowCount(4);
Font displayFont = new Font("Serif", Font.BOLD, 18);
sampleJList.setFont(displayFont);
JScrollPane listPane = new JScrollPane(sampleJList);

JPanel listPanel = new JPanelO;


listPanel.setBackground(Color.white);
Border listPanelBorder =
BorderFactory.createTitledBorder("Sample JList");
listPanel.setBorder(listPanelBorder);
listPanel.add(listPane);
content.add(listPanel, BorderLayout.CENTER);
JButton addButton =
new JButton("Add Entry to Bottom of JList");
addButton.setFont(displayFont);
addButton.addActionListener(new ItemAdder());
JPanel buttonPanel = new JPanelO;
buttonPanel.setBackground(Color.white);
Border buttonPanelBorder =
BorderFactory.createTitledBorder("Adding Entries");
buttonPanel.setBorder(buttonPanelBorder);
buttonPanel.add(addButton);
content.add(buttonPanel, BorderLayout.SOUTH);
pack () ;
setVisible(true);

private class ItemAdder implements ActionListener {

/** После щелчка на кнопке к ListModel добавляется новый


* пункт. Поскольку новый пункт может быть шире, чем
* старый (например, "Entry 10" и "Entry 9"),
* необходимо инициировать процедуру размещения элементов.
* Сделать это надо <1>перед</1> прокруткой.
V
public void actionPerformed(ActionEvent event) {
int index = sampleModel.getSize();
sampleModel.addElement("Entry " + (index+1));
((JComponent)getContentPane()).revalidate();
sampleJList.setSelectedlndex(index);
sampleJList.ensurelndexIsVisible(index);
}
612 Глава 15. Расширенные средства Swing

Sample JLIst

Entry 1 0 ^
Entry 11
[Entry 12 I

Adding Entries

[ Add Entry to Bottom of J l i s t Рис. 15.2. Использование объекта D e f a u l t L i s t M o d e l


I iTi T imi i-^mtri r
позволяет добавлять и удалять пункты списка J L i s t

Использование JList с произвольной моделью


данных
Вместо использования заранее определенной структуры, содержащей пункты спи­
ска, разработчик может создать собственную модель данных. Для этого необходимо
знать, как определять число элементов в структуре и как осуществлять чтение эле­
ментов. Для использования совместно с J L i s t модель данных, определенная разра­
ботчиком, должна содержать реализации четырех методов, объявленных в интер­
фейсе ListModel.

Интерфейс ListModel

public Object getElementAt(int index)


Данный метод возвращает элемент данных, соответствующий указанному индексу.

public int getSizeO


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

public void addListDataListener(ListDataListener listener)


Метод a d d L i s t D a t a L i s t e n e r добавляет к модели данных обработчик, который
оповещается о возникновении событий, связанных с выбором или отменой выбо­
ра пункта списка.

public void removeListDataListener(ListDataListener listener)


Этот метод разрывает связь модели данных с указанным обработчиком события.

Листинг 15.3 демонстрирует работу J L i s t с моделью данных, определенной раз­


работчиком. В данном примере используется набор объектов J a v a L o c a t i o n , описы­
вающих города или регионы с названием "Java". Вместо копирования набора объектов
мы определяем небольшой вспомогательный класс JavaLocationModel, код которо­
го приведен в листинге 15.4. Этот класс реализует интерфейс ListModel и предос­
тавляет методы для извлечения данных из набора. Созданную таким образом модель
данных мы передаем конструктору J L i s t , вызываемому в теле конструктора J l i s t -
CustomModel (листинг 15.3).
15.2. Компонент JList 613

В основе модели данных лежит набор J a v a L o c a t i o n C o l l e c t i o n (листинг 15.5)


объектов J a v a L o c a t i o n (листинг 15.6). Объекты J a v a L o c a t i o n содержат описание
городов и регионов с названием "Java", в частности страну, где расположен населен­
ный пункт, флаг страны и информацию о городах, находящихся по соседству.
Результаты выполнения данного примера показаны на рис. 15.3.

Листинг 15.3. JListCustomModel. Java

import java.awt.*;
import javax.swing.*;

/** Простой компонент J L i s t , иллюстрирующий использование


* объекта ListModel, определенного пользователем
* (JavaLocationListModel).

public class JListCustomModel extends JFrame {


public static void main(String[] args) {
new JListCustomModel0;
}
public JListCustomModel() {
superC'JList with a Custom Data Model");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
JavaLocationCollection collection =
new JavaLocationCollectionO ;
JavaLocationListModel listModel =
new JavaLocationListModel(collection);
JList sampleJList = new JList(listModel);
Font displayFont = new Font("Serif", Font.BOLD, 18);
sampleJList.setFont(displayFont);
content.add(sampleJList);
packO ;
setVisible(true);

Листинг 15.4.JavaLocationListModel.Java

import javax.swing.*;
import javax.swing.event.*;

/** Простой пример, демонстрирующий реализацию ListModel.


* Если вы хотите, чтобы пользователь мог добавлять и удалять
* элементы модели данных в процессе выполнения программы,
* надо создать класс AbstractListModel и добавить обработчик
* событий.
V
public class JavaLocationListModel implements ListModel {
614 Глава 15. Расширенные средства Swing

private JavaLocationCollection collection;

public JavaLocationListModel(JavaLocationCollection collection)


{
this.collection = collection;
}
public Object getElementAt(int index) {
return(collection.getLocations0 [index]);
}
public int getSizeO {
return(collection.getLocations().length);
}

public void addListDataListener(ListDataListener 1) {}

public void removeListDataListener(ListDataListener 1) {}

Листинг 15.5. JavaLocationCollection.Java

/** Простой набор данных, состоящий из экземпляров JavaLocation,


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

public class JavaLocationCollection {


private static JavaLocation[] defaultLocations =
{ new JavaLocation("Belgium",
"near Liege",
"flags/belgium.gif") ,
new JavaLocation("Brazil",
"near Salvador",
"flags/brazil.gif") ,
new JavaLocation("Colombia",
"near Bogota",
"flags/colombia.gif") ,
new JavaLocation("Indonesia",
"main island",
"flags/indonesia.gif") ,
new JavaLocation("Jamaica",
"near Spanish Town",
"flags/Jamaica.gif") ,
new JavaLocation("Mozambique",
"near Sofala",
"flags/mozambique.gif") ,
new JavaLocation("Philippines",
"near Quezon City",
"flags/philippines.gif") ,
new JavaLocation("Sao Tome",
"near Santa Cruz",
"flags/saotome.gif") ,
15.2. Компонент JList 615

new JavaLocation("Spain",
"near Viana de Bolo",
"flags/spain.gif"),
new JavaLocation("Suriname",
"near Paramibo",
"flags/suriname.gif"),
new JavaLocation("United States",
"near Montgomery, Alabama",
"flags/usa.gif"),
new JavaLocation("United States",
"near Needles, California",
"flags/usa.gif"),
new JavaLocation("United States",
"near Dallas, Texas",
"flags/usa.gif")
};
private JavaLocation[] locations;
private int numCountries;

public JavaLocationCollection(JavaLocation[] locations) {


this.locations = locations;
this.numCountries = countCountries(locations);
}
public JavaLocationCollection() {
this(defaultLocations);
}
public JavaLocation[] getLocations() {
return(locations);
}
public int getNumCountries() {
return(numCountries);
}
// Подсчет числа стран, представленных в наборе данных.
// Предполагается, что набор отсортирован по названиям стран,
private int countCountries(JavaLocation[] locations) {
int n = 0;
String currentCountry, previousCountry = "None";
for(int i=0;i<locations.length;i++) {
currentCountry = locations[i].getCountry();
if (IpreviousCountry.equals(currentCountry)) {
n++;
}
currentCountry = previousCountry;
}
return(n);
616 Глава 15. Расширенные средства Swing

Листинг 15.6.JavaLocation.Java

/** Простая структура данных, содержащая три поля: country,


* comment и flagFile. В полях содержатся строковые значения,
* представляющие страну, в которой есть город или регион
* под названием "Java," комментарии, уточняющие расположение
* населенного пункта внутри страны, и путь к графическом файлу
* с изображением флага страны. Данная структура используется
* в примерах, иллюстрирующих модель данных, определенную
* пользователем, и средства визуализации ячеек для JList.
V
public class JavaLocation {
private String country, comment, flagFile;

public JavaLocation(String country. String comment.


String flagFile) {
setCountry(country);
setComment(comment);
setFlagFile(flagFile);
}
/•• Строковое представление, используемое при выводе */

public String toStringO {


return("Java, " + getCountryO + " (" + getComment() + ").")

/** Определение названия страны, в которой находится город


или регион с названием "Java." */

public String getCountryO {


return(country);

/** С помощью данного метода можно задать название страны,


город в которой находится или регион с именем "Java." */

public void setCountry(String country) {


this.country = country;
}

/** Данный метод возвращает комментарий, предоставляющий


* дополнительную информацию о городе или регионе под
* названием "Java". Обычно информация представляется в
* форме "около < названия городов>".
*/

public String getComment() {


return(comment);
}
/** Данный метод позволяет задать комментарии,
предоставляющие дополнительную информацию
о городе или регионе с названием "Java". */
15.2. Компонент JList 617

public void setCoiranent(String comment) {


this.comment = comment;

/** Данный метод возвращает путь к файлу с


изображением флага страны. */

public String getFlagFile{) {


return(flagFile) ;

/** Данный метод позволяет задать путь к файлу с


изображением флага страны. */
public void setFlagFile(String flagFile) {
this.flagFile = flagFile;
}

Java» Belgimn (near Liege).


Java» Brazil (near Salvador).
Java» Colombia (near Bogota).
Java. Indonesia (hiain island).
Java» Jamaica (near Spanish Town).
Java» Mozambique (near Sofala).
Java» HdUppines (near Quezon City).
Java» Sao Tome (near Santa Cruz).
Java» Spain (near Viana de Bolo).
Java» Suriname (near Paramibo).
Java» United States (near Montgomery, Alabama). Рис. 15.3. Компонент J L i s t , использующий модель
Java, United States (near Needles, California). данных, определенную разработчиком, отображает
Java» United States (near Dallas, Texas). информацию, которая содержится в модели

JList и средства визуализации, определяемые


разработчиком
Вместо того чтобы принять способ отображения пунктов J L i s t , реализованный
по умолчанию, разработчик может определить графический компонент, предназна­
ченный для использования с пунктами списка. В обычных условиях для представле­
ния пунктов списка применяется объект J L a b e l , отображающий пункт как текстовую
строку. Если пункт списка представляет собой объект Icon, в составе J L a b e l выво­
дится изображение. Чтобы отобразить в одном пункте списка и пиктограмму, и текст,
надо создать новое средство представления данных из модели, которое реализует ин­
терфейс L i s t C e l l R e n d e r e r и возвращает компонент, предназначенный для ото­
бражения в J L i s t .
618 Глава 15. Расширенные средства Swing

Интерфейс ListCellRenderer
в интерфейсе L i s t C e l l R e n d e r e r объявлен единственный метод g e t L i s t -
CellRendererComponent.

public Component getListCellRendererComponent(JList list,


Object value,
int index,
boolean isSelected,
boolean cellHasFocus)
Метод g e t L i s t C e l l R e n d e r e r C o m p o n e n t проверяет объект, предназначенный
для отображения (value), и возвращает компонент, который должен быть пред­
ставлен в J L i s t . Объект J L i s t передается методу в качестве параметра. Это сде­
лано для того, чтобы свойства, определяющие внешний вид компонента
(например, цвет переднего плана и цвет фона), совпадали с соответствующими
свойствами списка. Оставшиеся параметры метода определяют индекс пункта спи­
ска, состояние пункта, т.е. информацию о том, выбран ли он, а также сведения о
том, имеет ли данный пункт фокус ввода.
Чтобы упростить разработку программ, Java 2 Platform предоставляет класс
Def a u l t L i s t C e l l R e n d e r e r , реализующий интерфейс L i s t C e l l R e n d e r e r . По умол­
чанию метод класса Def a u l t L i s t C e l l R e n d e r e r возвращает объект JLabel.
Рассматриваемый ниже пример представляет собой модификацию предыдущего
примера, где в модели данных содержались сведения о географических пунктах под
названием "Java". Сейчас мы добавим к модели данных средства отображения, с по­
мощью которых в списке будет выводиться не только информация о населенных
пунктах, но и флаг страны. В листинге 15.8 содержится код класса J a v a L o c a t i o n -
Renderer, реализующего интерфейс L i s t C e l l R e n d e r e r . Метод g e t L i s t C e l l ­
RendererComponent этого класса создает на базе данных, содержащихся в
J a v a L o c a t i o n , объект J L a b e l . Для связывания средств визуализации с J L i s t ис­
пользуется метод s e t C e l l R e n d e r e r (листинг 15.7).

Листинг 15.7.JListCustomRenderer.Java

import j a v a . a w t . * ;
import j a v a x . s w i n g . * ;
/** Простой компонент J L i s t , иллюстрирующий применение
* средств визуализации, определенных разработчиком
* (JavaLocationRenderer).
V
p u b l i c c l a s s JListCustomRenderer extends JFrame {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
new J L i s t C u s t o m R e n d e r e r 0 ;

p u b l i c JListCustomRenderer() {
s u p e r ( " J L i s t with a Custom Cell Renderer")
WindowUtilities.setNativeLookAndFeel();
15.2. Компонент JList 619

addWindowListener(new ExitListener());
Container content = getContentPane();

JavaLocationCollection collection =
new JavaLocationCollection0;
JavaLocationListModel listModel =
new JavaLocationListModel(collection);
JList sampleJList = new JList(listModel);
sampleJList.setCellRenderer(new JavaLocationRenderer())
Font displayFont = new Font("Serif", Font.BOLD, 18);
sampleJList.setFont(displayFont);
content.add(sampleJList) ;

packO ;
setVisible(true) ;
}
}

Вместо того чтобы принимать решение о том, каким должен быть цвет компонен­
та при выбранном пункте, при наличии фокуса и т.д., лучше создать подкласс класса
D e f a u l t L i s t C e l l R e n d e r e r (реализующего интерфейс L i s t C e l l R e n d e r e r ) и мо­
дифицировать возвращаемый объект J L a b e l , в котором цвета установлены коррект­
но. Например, в листинге 15.8 показано, как к объект)^ J L a b e l , возвращаемому мето­
дом s u p e r . g e t L i s t C e l l R e n d e r e r , добавляется объект I c o n . Текст, цвет переднего
плана и цвет фона J L a b e l остаются без изменений. Заметьте, что при каждом щелчке
на пункте списка вызывается метод g e t L i s t C e l l R e n d e r e r C o m p o n e n t , поэтому для
повышения производительности желательно сохранять в памяти внутренние компо­
ненты, которые генерируются при построении возвращаемых компонентов. В дан­
ном примере при каждом изменении пункта списка средства визуализации использу­
ют для отображения в J L a b e l флага страны объект Image I c o n . Чтобы увеличить бы­
стродействие программы, создается хеш-таблица, в которой объекты Image I c o n
связываются с объектами J a v a L o c a t i o n . Если объект J a v a L o c a t i o n уже отобра­
жался, вместо генерации нового Image I c o n используется объект, созданный ранее.
Внешний вид модифицированного списка J L i s t , который использует средства ви­
зуализации, созданные разработчиком, показан на рис. 15.4.'

Листинг 1 5 . 8 . J a v a L o c a t i o n R e n d e r e r . J a v a

import javax.swing.*;
import java.awt.*;
import java.util.*;

/** Простое с р е д с т в о в и з у а л и з а ц и и э л е м е н т о в . Вместо п о с т р о е н и я


* н о в о г о к л а с с а " с н у л я " р а с ш и р я е т с я существующий к л а с с .
* Преимущество д а н н о г о подхода с о с т о и т в том, ч т о п о д с в е т к а
* при выборе пунктов и отображение и з в е с т н ы х типов
* осуществляются а в т о м а т и ч е с к и . Недостатком я в л я е т с я тот ф а к т ,
* ч т о ваши возможности ограничены компонентами J L a b e l ,
* которые возвращают с р е д с т в о просмотра по умолчанию.
* <Р>
* Приведенный ниже метод может вызываться много раз в
620 Глава 15. Расширенные средства Swing

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


* повторно генерировать объекты Imagelcon, поэтому создали
* хеш-таблицу Hashtable, в которой связываются ранее
* выведенные пункты с пиктограммами. Пункты, которые
* создавались ранее, используются повторно.
* <Р>
* В первых реализациях JDK 1.2 средства визуализации,
* используемые по умолчанию, содержали ошибку: пиктограммы
* при работе с последующими пунктами не очищались. Поэтому
* если вы работаете как со строками текста, так и с Imagelcon,
* в объекте JList строки будут представлены пиктограммами. В
* в данном примере, если значение value не является экземпляром
* JavaLocation, старая пиктограмма удаляется.

public class JavaLocationRenderer extends


DefaultListCellRenderer {
private Hashtable iconTable = new Hashtable();

public Component getListCellRendererComponent(JList list.


Object value,
int index,
boolean isSelected,
boolean hasFocus) {
// Сначала создается объект JLabel, содержащий текст, а затем
// добавляется изображение.

JLabel label =
(JLabel)super.getListCellRendererComponent(list,
value,
index,
isSelected,
hasFocus) ;
if (value instanceof JavaLocation) {
JavaLocation location = (JavaLocation)value;
Imagelcon icon = (Imagelcon)iconTable.get(value);
if (icon == null) {
icon = new Imagelcon(location.getFlagFile0);
iconTable.put: (value, icon) ;
}
label.setlcon(icon);
} else {
// Очистка старой пиктограммы; это необходимо сделать
// при работе с первыми реализациями JDK 1.2.
label.setlcon(null);
}
return(label);
}
15.3. JTree 621

шшвшшшшшшвш
• Java, Belfiiini (near Liege).

^ Д Д Java, Brazil (near Salvador).

I M H J Java, Colombia (near Bogota).

Java, Indonesia (nniin island).

• j ^ l Java, Jamaica (near Spanish Town).

N H H Java, Mozambique (near Sofala).

Java, Philippines (near Quezon City).

Java, Sao Tome (near Santa Cruz).

Java, Spain (near Viana de Bolo).

Java, Suriname (near Paramibo).

Java, United States (near Montgomery, Alabama).


Рис. 15.4. В JList содержимое пунктов
Java, United States (near Needles, California).
списка не ограничивается текстовыми
строками. В составе списка может быть
Java, United States (near Dallas, Texas).
представлен любой Swing-компонент

15.3. JTree
Компонент J T r e e отображает элементы данных (узлы) в виде иерархической
структуры. В данном разделе мы рассмотрим основы использования JTree, особен­
ности обработки событий, связанные с выбором узлов, обсудим пример модели дре­
вовидной структуры, определяемой пользователем (дерева, допускающего создание
дочерних узлов в процессе работы), и покажем, как заменить пиктограммы, пред­
ставляющие узлы дерева, на изображения, определенные разработчиком.

Простой объект JTree


Наиболее простой и часто используемый способ построения JTree предполагает
создание объектов Def aultMutableTreeNode, выполняющих роль узлов дерева. Уз­
лы, не имеющие дочерних узлов, отображаются как листы древовидной структуры.
Если у узла есть дочерние узлы, он отображается как папка. С узлом дерева можно свя­
зать любой объект, для этого надо передать конструктору класса De f a u l t Ми t a b l e -
TreeNode значение, интерпретируемое как "пользовательский объект". Надписи ря­
дом с узлами дерева представляются как объекты S t r i n g , поэтому перед отображе­
нием объекта надо вызвать метод t o S t r i n g .
Построив несколько узлов, их надо связать в древовидную структуру, используя
для этого вызов parentNode . add (childNode). Корневой узел структуры передается
конструктору JTree. Поскольку размеры дерева могут изменяться в результате дейст-
622 Глава 15. Расширенные средства Swing

ВИЙ пользователя (при разворачивании и сворачивании узлов), объект JTree обычно


помещают в панель JScrollPane. Код, реализующий чрезвычайно простое дерево,
приведен ниже.
DefaultMutableTreeNode root =
new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode childl =
new DefaultMutableTreeNode("Child 1"};
root.add(childl);
DefaultMutableTreeNode child2 =
new DefaultMutableTreeNode("Child 2 " ) ;
root.add(child2);
JTree tree = new JTree(root);
someWindow.add(new JScrollPane(tree));
Связывание вручную сложных деревьев — рутинная процедура, требующая много
времени и усилий. Поэтому при построении дерева желательно сначала создать соот­
ветствующую структуру данных, а затем, используя эту структуру, автоматически соз­
дать и объединить узлы.

Конструкторы класса DefaultMutableTreeNode


Класс D e f a u l t M u t a b l e T r e e N o d e содержит три конструктора:

public D e f a u l t M u t a b l e T r e e N o d e ( )
public D e f a u l t M u t a b l e T r e e N o d e ( O b j e c t data)
public D e f a u l t M u t a b l e T r e e N o d e ( O b j e c t data, b o o l e a n allowChildren)
Bee приведенные конструкторы предназначены для создания узла дерева. Первый
конструктор формирует "пустой" узел, не содержащий данных. Второй конструк­
тор позволяет задавать данные. Т р е т и й конструктор дает возможность явно опре­
делить, может ли данный узел содержать дочерние узлы. По умолчанию наличие
дочерних узлов допускается.

М е т о д ы класса D e f a u l t M u t a b l e T r e e N o d e
Класс D e f a u l t M u t a b l e T r e e N o d e содержит 50 методов, предназначенных для мо­
дификации дочерних и родительских узлов, а также для определения относитель­
ного расположения узла в иерархии. Наиболее часто используемые методы описа­
ны ниже.

public void a d d ( M u t a b l e T r e e N o d e child)


public void r e m o v e ( M u t a b l e T r e e N o d e child)
Указанные методы предназначены соответственно для добавления и удаления до­
чернего узла.

public E n u m e r a t i o n children()
Метод c h i l d r e n возвращает объект E n u m e r a t i o n , содержащий непосредствен­
ных потомков данного узла. Если узел не имеет дочерних узлов, возвращается зна­
чение n u l l .
1 5 , 3 . JTree 623

public int getChildCountO


Метод g e t C h i l d C o u n t возвращает количество непосредственных потомков дан­
ного узла.

public TreeNode getParent()


public TreeNode getRoot()
Метод g e t P a r e n t возвращает узел, являющийся родительским для данного узла.
Если родительского узла не существует, возвращается значение n u l l . Метод
getRoot возвращает корневой узел дерева, содержащего данный узел.

public boolean isLeaf()


public boolean isRoot()
Указанные два метода позволяют определить, является ли узел листом и является
ли он корневым узлом дерева.

public void removeAllChildrenO


Метод r e m o v e A l l C h i l d r e n удаляет всех потомков данного узла.

В листинге 15.9 данные, предназначенные для оформления в виде дерева, содер­


жатся в структуре, состоящей из вложенных массивов. При построении дерева каж­
дый элемент массива связывается с узлом посредством метода p r o c e s s H i e r a r c h y .
Если элемент массива сам является массивом ( i n s t a n c e o f O b j e c t [ ] ) , метод
p r o c e s s H i e r a r c h y вызывается рекурсивно для построения поддерева. На рис. 15.5,а
показано исходное дерево, а на рис. 15.5,6 — то же дерево с развернутыми папками.

Листинг 15.9.SimpleTree.Java

import j ava . awt.''^;


import j a v a x . s w i n g . * ;
import j a v a x . s w i n g . t r e e . * ;
/** Пример древовидной структуры, построенной из
объектов DefaultMutableTreeNode. */
p u b l i c c l a s s SimpleTree extends JFrame {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
new S i m p l e T r e e ( ) ;
}

p u b l i c SimpleTree0 {
s u p e r ( " C r e a t i n g a Simple J T r e e " ) ;
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new E x i t L i s t e n e r ( ) ) ;
Container c o n t e n t = g e t C o n t e n t P a n e ( ) ;
Object[] h i e r a r c h y =
{ "javax.swing",
"javax.swing.border",
"javax.swing.colorchooser",
624 Глава 15. Расширенные средства Swing

"j avax.swing.event",
"javax.swing.filechooser",
new Object[] { "javax.swing.plaf",
"javax.swing.plaf.basic",
"javax.swing.plaf.metal",
"javax.swing.plaf.multi" ) ,
"javax.swing.table",
new Object[] { "javax.swing.text",
new Object[] { "javax.swing.text.html".
• j avax.swing.text.html.parser" },
"javax.swing.text.rtf" },
"javax.swing.tree"
"javax.swing.undo" };
DefaultMutableTreeNode root » processHierarchy(hierarchy);
JTree tree = new JTree(root);
content.add(new JScrollPane(tree), BorderLayout.CENTER);
setSize(275, 300);
setVisible(true);
}\
/** Простая процедура, оформляющая первый элемент массива
* в виде узла древовидной структуры. Остальные элементы
* массива определяются как узлы, дочерние по отношению
* к первому. Процесс повторяется рекурсивно для всех
* элементов, являющихся, в свою очередь, массивами.
V
private DefaultMutableTreeNode processHierarchy(
Object[] hierarchy) {
DefaultMutableTreeNode node =
new DefaultMutableTreeNode(hierarchy[0]);
DefaultMutableTreeNode child;
for(int i=l; i<hierarchy.length; i++) {
Object nodeSpecifier = hierarchy[i];
if (nodeSpecifier instanceof Object[]) { //Node with children
child = processHierarchy((Object[])nodeSpecifier);
} else {
child = new DefaultMutableTreeNode(nodeSpecifier); //Leaf
}
node.add(child);
}
return(node);
15.3.JTree 625

|fflflHI'l,ll.|[|IHIilMli>mtM^Msi
:1J javax. swing . J javax swing
' • javax.swing.border » javax.swing.border
• » javax svvlng.colorchooser • lavaxswing.coiorchooser
' ш javax.swing.event • javax swing.event
[ « javax.8wing.filechooser % javax.swJng.mechooser
if lJi9vax.swing.plaf - 1 javax.8wing.plaf
I • javaxswing.table » javax.swingplafbasic
s- _I;j javax.swing.text « javax.swing.plafmetal
i • javax.swlng.tree • javax.swlng.plafmulti
' • javaxswlng.undo # javaxswing.table
- ^ javax.swing.text
f javax.swing.lext.html
« javax.swlng.text.html.parser
• Javax.swing,text,rtf
Ф javax swing.tree
» Javax.8wing.undo

(a) (6)
Рис. 15.5. Компонент Jtree: a— папки закрыты; б— в результате
действий пользователя папки развернуты

Перед тем как приступать к обсуждению динамической модификации деревьев,


рассмотрим конструкторы и основные методы класса JTree.

Конструкторы JTree
lOiacc JTree предоставляет семь различных конструкторов. Два из них, которые
применяются наиболее часто, описаны ниже. Другие, менее популярные конструкто­
ры позволяют задавать информацию в виде объектов Vector или Hash t a b l e . В клас­
се JTree также определен конструктор без параметров, однако при создании он за­
полняется данными, предоставляемыми Sun, и годится только для написания проб­
ных примеров.

public JTree(TreeNode root)


public JTree(TreeModel model)
Первый конструктор создает древовидную структуру, корнем которой является
указанный узел. Второй конструктор создает дерево, соответствующее указанной
модели.

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


Класс JTree насчитывает более ПО методов. Многие из них предназначены для
редактирования и работы с выбранными ветвями дерева. Ряд методов генерирует со­
бытия TreeExpansionEvent. Полный список методов описан в разделе API, посвя­
щенном классу j a v a x . swing. JTree.

Обработка событий JTree


Для поддержки событий, связанных с выбором узлов, надо связать с компонентом
JTree объект T r e e S e l e c t i o n L i s t e n e r . Интерфейс T r e e S e l e c t i o n L i s t e n e r тре­
бует реализации единственного метода valueChanged. При обработке события
T r e e S e l e c t i o n E v e n t определить выбранный узел можно с помощью метода
t r e e . g e t L a s t S e l e c t e d P a t h C o m p o n e n t . Тип возвращаемого объекта надо привести
626 Глава 15. Расширенные средства Swing

к типу узла (обычно это Def aultMutableTreeNode). Для извлечения данных, содер­
жащихся в узле, надо использовать метод getUserObject. Если же вам нужен лишь
текст, соответствующий данному узлу, достаточно вызвать метод toString объекта,
возвращаемого в результате вызова tree . getLastSelectedPathComponent.
В листинге 15.10 приведен код, в котором создается древовидная структура. Каж­
дый узел этой структуры имеет три дочерних узла. С компонентом JTree связан об­
работчик TreeSelectionListener. Когда пользователь выбирает узел, в поле ре­
дактирования, расположенном в нижней части окна, отображается значение, полу­
ченное в результате вызова метода toString узла (рис. 15.6).

Листинг 15.10.SelectableTree.Java

import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax. swing, event.'^;

/** Данный объект JTree сообщает о выбранном объекте, помещая


* in а JTextField.

public class SelectableTree extends JFrame


implements TreeSelectionListener {
public static void main(String[] args) {
new SelectableTree0;

private JTree tree;


private JTextField currentSelectionField;
public SelectableTree() {
superC'JTree Selections");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
DefaultMutableTreeNode root =
new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode child;
DefaultMutableTreeNode grandchild;
for(int childlndex=l; childlndex<4; childlndex++) {
child = new DefaultMutableTreeNode("Child " + childlndex);
root.add(child);
for(int grandChildIndex=l; grandChildIndex<4;
grandChildIndex++) {
grandchild =
new DefaultMutableTreeNode("Grandchild " + childlndex +
"." + grandChildlndex);
child.add(grandchild);
}
}
tree = new JTree(root);
tree.addTreeSelectionListener(this);
content.add(new JScrollPane(tree), BorderLayout.CENTER);
currentSelectionField =
15.3. JTree 627

new JTextField{"Current Selection: NONE");


content.add(currentSelectionField, BorderLayout.SOUTH);
setSize(250, 275) ;
setVisible(true) ;

public void valueChanged(TreeSelectionEvent event) {


Object selection = tree.getLastSelectedPathComponent();
if (selection != null) {
currentSelectionField.setText
("Current Selection: " + selection.toStringO);
}

Htfflwmw
„ J Root
\щтшшвш^шшш
„JRoot i
* . ? Child 1 - J Child 1
•^ I Child 2 « Grandchild 11
+ . I Child 3 • Grandchild 12
# Grandchild 1.3 i
- % Child 2 il
• Grandchild 2.1 il

« Grandchild 23 ::
- <5 Child 3
# Grandchild 31
• Grandchild 32
• Grandchild 3.3 |

{Current Selection NONE jCurrent Selection Grandchild 2.2 |


(a) (6)
Р1лс. 15.6. Компонент J t r e e : a— исходное состояние; 6- состояние
после выбора дочернего узла

Событие T r e e E x p a n s i o n E v e n t генерируется при развертывании и свертывании


узла дерева. Для обработки T r e e E x p a n s i o n E v e n t s надо связать с компонентом
J T r e e объект T r e e E x p a n s i o n L i s t e n e r . В обработчике определены два метода
( t r e e E x p a n d e d и t r e e C o l l a p s e d ) , которые вызываются соответственно при раз­
вертывании и свертывании дерева. Объект T r e e E x p a n s i o n E v e n t содержит метод
g e t P a t h , который возвращает путь от корневого узла к узлу, сгенерировавшему со­
бытие. Путь оформляется в виде объекта P a t h . Для определения источника события
используется метод g e t L a s t P a t h C o m p o n e n t объекта P a t h .

Модели, определяемые разработчиком, и динамические


деревья
Для получения информации компонент J T r e e использует объект T r e e M o d e l . Как
и при работе с компонентом J L i s t , вы можете заменить модель на новую. В новой
модели надо указать, каким образом должны извлекаться данные. Пример использо­
вания данного подхода был рассмотрен выше при обсуждении компонента J L i s t .
Объект T r e e M o d e l содержит информацию о корневом узле дерева и определяет
методы, предназначенные для доступа к дочерним объектам и поддержки обработчи­
ков. Поскольку узлы дерева взаимосвязаны, при построении древовидной структуры
разработчики обычно оставляют класс, реализующий интерфейс T r e e M o d e l , без из-
628 Глава 15. Расширенные средства Swing

менений, а вместо этого создают новый класс TreeNode. Проще всего сделать это,
создавая подкласс класса DefaultMutableTreeNode, обеспечивающего поддержку
родительского узла и нескольких дочерних узлов.
Ниже мы рассмотрим часто используемые методы класса D e f a u l t M u t a b l e ­
TreeNode и приведем пример узла, определенного пользователем, построенного как
подкласс класса DefaultMutableTreeNode.

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


в классе DefaultMutableTreeNode определено более 50 методов, предназначенных
для доступа к узлам дерева. Ссылки на дочерние узлы хранятся в объекте Vector, содер­
жащемся в классе. Методы, которые используются наиболее часто, приведены ниже.

public void add(MutableTreeNode child)


public void remove(MutableTreeNode child)
Указанные два метода позволяют добавлять и удалять дочерние узлы. Ссылка на
узел добавляется в конец вектора (объекта Vector).

public void insert(MutableTreeNode child, int index)


public void remove(int index)
Метод i n s e r t включает ссылку на дочерний узел; расположение ссылки задается
параметром index. Существующие ссылки с индексами, не ниже указанного, сдви­
гаются на одну позицию. Наибольший допустимый индекс определяется размером
объекта Vector. Для того чтобы выяснить, сколько дочерних узлов связано с те­
кущим узлом, надо вызвать метод getChildCount. Метод remove удаляет дочер­
ний узел, соответствующий заданному индексу. Если индекс лежит за пределами
вектора, генерируется исключение ArraylndexOutOf BoundsException.

public Enumeration children()


Этот метод возвращает объект Enumeration, содержащий дочерние узлы данного
элемента дерева.

public TreeNode getChildAt(int index)


public int getIndex(TreeNode child)
Метод g e t C h i l d A t возвращает дочерний узел, соответствующий указанному ин­
дексу. Если значение индекса лежит за пределами вектора, содержащего ссылки на
дочерние узлы, генерируется исключение ArraylndexOutOfBoundsException.
Метод g e t Index возвращает либо индекс, соответствующий указанному узлу, либо
значение -1, если узел не является дочерним для данного узла.

public int getChildCountO


Метод getChildCount возвращает число дочерних узлов для данного узла.

public TreeNode getParent()


Метод g e t P a r e n t возвращает родительский узел для данного узла. Если родитель­
ский узел отсутствует, возвращается значение n u l l .
15.3.JTree 629

public T r e e N o d e [ ] getPath()
Метод g e t P a t h возвращает массив узлов, представляющий путь от корневого до
данного узла.

public T r e e N o d e getRoot()
Данный метод возвращает корневой узел дерева.

public b o o l e a n isLeaf()
public b o o l e a n isRoot()
Метод i s L e a f возвращает значение t r u e , если узел является листом (т.е. не имеет
дочерних узлов); в противном случае возвращается значение f a l s e . Метод
i s R o o t возвращает значение t r u e , если данный узел является корневым узлом
дерева; в противном случае возвращается значение f a l s e .
Возможно, вы захотите, чтобы дерево строилось динамически, т.е. узлы создава­
лись в ответ на действия пользователя. В этом случае узел, определяемый разработ­
чиком, можно построить на базе Def a u l t M u t a b l e T r e e N o d e . Первоначально в состав
дерева включаются не все узлы, а лишь те, которые должны непосредственно ото­
бражаться на экране. В этом случае необходимо разработать алгоритм для описания
дочерних узлов, которые генерировались бы только при разворачивании папки.
В листинге 15.11 показан код, при выполнении которого строится дерево, пред­
ставляющее иерархическую нумерацию пунктов. Корневому узлу соответствует номер
1, дочерним узлам первого уровня — номера 1.1, 1.2, 1.3 и т.д. Дочерним узлам второго
уровня соответствуют номера 1.1.1, 1.1.2 и т.д. Число дочерних узлов для каждого узла
задается в качестве параметра командной строки при вызове программы.
При динамическом построении J T r e e перед отображением дочерних узлов вызы­
вается метод g e t C h i l d C o u n t . Специальный флаг указывает, были ли узлы построены
раньше. В теле метода g e t C h i l d C o u n t , если значение флага равно f a l s e , строятся
дочерние узлы и добавляются к текущему узлу. Ч т о б ы предотвратить попытки под­
счета дочерних узлов (и соответственно их построения) при определении, является
ли узел листом, в примере переопределен метод i s Leaf, который всегда возвращает
значение f a l s e . Реализация данного подхода продемонстрирована в листинге 15.12,
в котором представлен код для динамического построения узлов. Результаты выпол­
нения программы показаны на рис. 15.7.

Листинг 1 5 . 1 1 . DynamiCTree. Java

import java.awt.*;
import javax.swing.*;

/** Пример и с п о л ь з о в а н и я древовидной с т р у к т у р ы , в которой


* дочерние элементы с т р о я т с я в п р о ц е с с е выполнения программы.
* Древовидная с т р у к т у р а р е а л и з о в а н а в к л а с с е O u t l i n e N o d e .
V

p u b l i c c l a s s DynamicTree e x t e n d s JFrame {
public s t a t i c void main(String[] args) {
i n t n = 5; / / Число дочерних у з л о в для каждого у з л а .
630 Глава 15. Расширенные средства Swing

if (args.length > 0) {
try (
n = Integer.parseint(args[0]);
} catch(NumberFormatException nfe) {
System.out.println(
"Can't parse number; using default of " + n ) ;
}
}
new DynamicTree(n);
}

public DynamicTree(int n) {
super("Creating a Dynamic JTree");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
JTree tree = new JTree(new OutlineNode(1, n ) ) ;
content.add(new JScrollPane(tree), BorderLayout.CENTER);
setSize (300, 475);
setVisible(true);
}

Листинг 15.12.OutlineNode.Java

import java.awt.^;
import javax.swing.*;
import javax.swing.tree.*;

/** Простой объект TreeNode, предназначенный для построения


* дочерних узлов в процессе выполнения программы.
* Перед обращением к реальному дочернему узлу вызывается
* метод getChildCount. Если дочерний узел отсутствует,
* getChildCount строит его.
* <Р>
* Таким образом, в начале выполнения программы строятся
* лишь "очертания" дерева. Если в начальный момент времени
* текущим узлом является "х", то дочерние узлы - "х.О",
* "х.1", "х.2" и "х.З".
•к <р>

public class OutlineNode extends DefaultMutableTreeNode {


private boolean areChildrenDefined = falser-
private int outlineNum;
private int numChildren;

public OutlineNode(int outlineNum, int numChildren) {


this.outlineNum = outlineNum;
this.numChildren = numChildren;

public boolean isLeaf() {


return(false);
15.3.JTree 631

public int getChildCount() {


if (!areChildrenDefined) {
defineChildNodes0;
}
return(super.getChildCount());
}

private void defineChildNodes() {


// Если вы используете "add" для добавления новых дочерних
// узлов, то должны сначала установить флаг. Если не сделать
// этого, при вызове getChildCount возникнет бесконечная
// последовательность рекурсивных вызовов.
areChildrenDefined = true;
for (int i=0; KnumChildren; i++) {
add(new OutlineNode(i+l, numChildren));
}
}
public String toStringO {
TreeNode parent = getParentO;
if (parent == null) {
return(String.valueOf(outlineNum));
} else {
return(parent.toString() + "." + outlineNum);
}
}

^Creating a Dynamic JTree


11
1
шт
2
.*~_I1 3
г 11 4
i141
J 1 4 2
1143
1144
^ ™J1 4 4 1
•• _J 1 4 4 2
•• И 443
- 11444
* _j 1 4 4 4 1
+ J14442
+ n 4443
- ^ 1 4444
+ _ J 1 4 4.4.4.1
+ M 4 4.4.4.2
+ ;1 4 4.4.4.3
+ 14 4.4.4.4
+ 1 4 4.4.4.5
+ M 4445
' ™J1 4 4 5
П45
5

(6)
Рис. 15.7. Объект TreeNode, определяемый разработчиком, допускает дина­
мический рост древовидной структуры: а — исходное дерево; 6 — узлы,
динамически построенные при развертывании папок
632 Глава 15. Расширенные средства Swing

Замена пиктограмм в узлах дерева


Компонент JTree позволяет изменять пиктограммы, представляющие неразвернутые
узлы, не являющиеся листами, развернутые узлы и листы дерева. Для замены объекта
Image Icon, отображаемого в компоненте JTree, надо создать экземпляр
DefaultTreeCellRenderer и вызвать методы setOpenlcon, setClosedlcon и
setLeaf Icon, передавая им либо требуемый объект Icon, либо значение null, если пик­
тограмма отображаться не должна. Объект Image Icon создается на базе графического
файла, содержащего изображение небольшого размера. Экземпляр класса Default­
TreeCellRenderer связывается с деревом посредством метода setCellRenderer.
В листинге 15.13 показан код, который создает три отдельных дерева. В первом из
них отображаются пиктограммы, принятые по умолчанию, во втором вывод пикто­
грамм запрещен, а в третьем для представления узлов используются пиктограммы,
определенные разработчиком. Внешний вид деревьев показан на рис. 15.8.

Листинг 15.13.Customlcons.Java

import java.awt.*;
import Java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
/** JTree без пиктограмм в узлах дерева или с
пиктограммами, определенными пользователем. */

public class Customlcons extends JFrame {


public static void main(String[] args) {
new Customlcons();

private Icon customOpenlcon =


new Image I con (" image s/Circle__l. gif ") ;
private Icon customClosedlcon =
new Imagelcon("images/Circle_2.gif");
private Icon customLeafIcon =
new Imagelcon("images/Circle_3.gif");

public Customlcons() {
super("JTree Selections");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
content.setLayout(new FlowLayout());
DefaultMutableTreeNode root =
new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode child;
DefaultMutableTreeNode grandchild;
for(int childlndex=l; childlndex<4; childlndex++) {
child = new DefaultMutableTreeNode("Child " + childlndex);
root.add(child);
for(int grandChildIndex=l; grandChildIndex<4;
grandChildIndex++) {
grandchild =
15.3. JTree 633

new DefaultMutableTreeNode("Grandchild " +


childlndex +
"." + grandChildlndex);
child.add(grandchild);
}
)

JTree treel = new JTree(root);


treel.expandRow(1); // Расширение дочерних узлов для
// демонстрации пиктограмм.
JScrollPane panel = new JScrollPane(treel);
panel.setBorder(
BorderFactory.createTitledBorder("Standard Icons")
content.add(panel);

JTree tree2 = new JTree(root);


// Расширение дочерних узлов для демонстрации пиктограмм.
tree2.expandRow(2);
DefaultTreeCellRenderer renderer2 =
new DefaultTreeCellRenderer0;
renderer2.setOpenlcon(null);
renderer2.setClosedlcon(null);
renderer2.setLeafIcon(null);
tree2.setCellRenderer(renderer2);
JScrollPane pane2 = new JScrollPane(tree2);
pane2.setBorder(
BorderFactory.createTitledBorder("No Icons"));
content.add(pane2);

JTree treeS = new JTree(root);


// Расширение дочерних узлов для демонстрации пиктограмм.
tree3.expandRow(3);
DefaultTreeCellRenderer rendererS =
new DefaultTreeCellRenderer0;
rendererS.setOpenlcon(customOpenlcon);
rendererS.setClosedlcon(customClosedlcon);
rendererS.setLeafIcon(customLeafIcon);
treeS.setCellRenderer(rendererS);
JScrollPane рапеЗ = new JScrollPane(tree3);
рапеЗ.setBorder(
BorderFactory.createTitledBorder("Custom Icons"));
content.add(рапеЗ);

packO ;
setVisible(true);
}
634 Глава 15. Расширенные средства Swing

iHPiffuwffiimi
standard icons No (cons ' Custom teo«s
, s Root Root ( f ) Root
- „jChildl • Child 1 + Г2) Child 1
• Grandchild i 1 - Child 2 + ^ r ) Child 2
« Grandchild 1 2 Grandchild 21 - M Child 3
« Grandchild 1 3 Grandchild 2 2 (3) Grandchild 3 1
• _ J Child 2 Grandchild 2 3 f i } Grandchild 3 2
* J Child 3 .+ Child 3 ( ? ) Grandchild 3.3

Рис. 15.8. Пример замены пиктограмм


в объекте JTree

15.4. JTable
Компонент J T a b l e отображает данные в виде двухмерной таблицы, состоящей из
строк и столбцов. Данный компонент— самый сложный в Swing API. Работа J T a b l e
базируется на трех моделях: модели таблицы, модели столбца и модели выбора спи­
ска. Модель таблицы обрабатывает данные в ячейках таблицы, модель столбца управ­
ляет добавлением и удалением столбцов таблицы, а модель выбора списка позволяет
выбирать строки таблицы (выбор столбцов поддерживается моделью столбца).
Каждая из перечисленных моделей реализована отдельно. Все три реализации
хранятся в пакете j a v a x . s w i n g , t a b l e . В данном разделе мы рассмотрим модель
таблицы, используемую по умолчанию, и модель, определяемую разработчиком. Под­
робную информацию о модели столбцов и модели выбора вы найдете в книге Кима
Топли (Kim Topley) CoreJava Foundation Classes.

Простой способ создания JTable


Простейший способ создания компонента J T a b l e состоит в передаче конструк­
тору двухмерного массива строк, определяющих содержимое ячеек таблицы, и одно­
мерного массива строк, представляющих собой заголовки столбцов. Например, сле­
дующий фрагмент кода создает таблицу, содержащую N строк и М столбцов:
String[][] data = { { "Cell (1,1)", --г "Cell ( 1 , N ) " },

{ " C e l l ( M , l ) " , . . . , " C e l l (M,N)" } };


S t r i n g [ ] columnNames = { "Column 1 " , . . . , "Column N" };
J T a b l e t a b l e = new J T a b l e ( d a t a , columnNames);
В качестве заголовков столбцов и содержимого ячеек могут также быть использова­
ны произвольные объекты Obj e c t , поскольку каждый объект представляется как ком­
понент J L a b e l , содержимое которого определяется с помощью метода t o S t r i n g .
Подобно J L i s t и J T r e e , таблицы часто помещаются в панель с прокруткой. Если
строка прокрутки не используется, заголовки столбцов не будут отображаться. Разме­
ры панели определяются отображаемыми размерами таблицы, которые по умолча­
нию равны 450x400 пикселей. Для того чтобы изменить отображаемые размеры таб-
15.4. JTable 635

лицы, надо передать соответствующий объект D i m e n s i o n методу setPreferred-


S c r o l l a b l e V i e w p o r t S i z e объекта J T a b l e .

Ha заметку
Чтобы заголовки JTable отображались на экране, компонент должен
быть помещен в состав объекта JScrollPane,
Ш
Конструкторы JTable
в классе J T a b l e определено семь конструкторов. Если данные для ячеек не зада­
ны, ячейки таблицы заполняются значениями n u l l . П о умолчанию заголовки столб­
цов создаются по соглашению, принятому в большинстве электронных таблиц (А, В,
С, АА, ВВ, СС, ...). Пять наиболее часто используемых конструкторов J T a b l e описа­
ны ниже. Всеми конструкторами, кроме последнего, таблица создается с использова­
нием D e f a u l t T a b l e M o d e l , D e f a u l t C o l u m n M o d e l и D e f a u l t L i s t S e l e c t i o n M o d e l .

public JTableO
Данный конструктор создает пустую таблицу с внутренними моделями Def a u l t ­
T a b l e M o d e l , D e f a u l t C o l u m n M o d e l и D e f a u l t L i s t S e l e c t i o n M o d e l . Для запол­
нения таблицы используются методы addRow и addColumn. По умолчанию каждая
ячейка таблицы допускает редактирование содержимого.

public JTable(int rows, int c o l u m n s )


Данный конструктор действует так же, как и предыдущий, за исключением того,
что создаваемая таблица содержит указанное число строк и столбцов (ячейки за­
полняются значениями n u l l ) .

public JTable(Object[][] data, Object[] c o l u m n N a m e s )


Указанный конструктор позволяет заполнять ячейки таблицы и задавать имена
столбцов. Содержимое ячеек и имена столбцов представляются как объекты
J L a b e l . Если не определены специальные средства визуализации информации,
перед отображением содержимого ячеек вызываются методы t o S t r i n g объектов.
Специальные средства отображения можно использовать, в частности, для пред­
ставления в ячейках таблицы объектов B o o l e a n , Image I c o n и Number.

public JTable(Vector data. Vector c o l u m n N a m e s )


Этот конструктор позволяет задавать содержимое ячеек и имена столбцов с помо­
щью объектов V e c t o r . При определении данных каждый элемент вектора должен, в
свою очередь, представлять собой объект V e c t o r , описывающий строку таблицы.

public JTable(TableModel m o d e l )
Данный конструктор создает объект J T a b l e , в котором строки и столбцы опреде­
ляются с помощью параметра T a b l e M o d e l . Чаще всего модель таблицы строится
на основе A b s t r a c t T a b l e M o d e l либо Def a u l t T a b l e M o d e l ; каждый из этих объ­
ектов генерирует события T a b l e M o d e l E v e n t . Таблица также содержит D e f a u l t ­
ColumnModel и D e f a u l t L i s t S e l e c t i o n M o d e l .
636 Глава 15. Расширенные средства Swing

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


в классе J T a b l e определено более 125 методов, но многие из них дублируются
моделями, на базе которых строятся объекты J T a b l e : T a b l e M o d e l , T a b l e C o l u m n -
Model и L i s t S e l e c t i o n M o d e l . Ссылки на модели содержатся в переменных J T a b l e ,
объявленных как p r o t e c t e d , и многие из методов класса J T a b l e попросту обраща­
ются к соответствующим методам моделей. Ниже приведено описание некоторых из
методов класса J T a b l e .

public T a b l e M o d e l getModel()
public v o i d s e t M o d e l ( T a b l e M o d e l tableModel)
Метод g e t M o d e l возвращает, a метод s e t M o d e l устанавливает объект T a b l e -
Model. Объект T a b l e M o d e l содержит данные, отображаемые в ячейках J T a b l e .

public T a b l e C o l u m n M o d e l g e t C o l u m n M o d e l ( )
public s e t C o l u m n M o d e l ( T a b l e C o l u m n M o d e l c o l u m n M o d e l )
Метод g e t ColumnModel возвращает, a метод s e t C o l u m n M o d e l устанавливает
объект T a b l e C o l u m n M o d e l для таблицы. По умолчанию модель столбцов создает­
ся автоматически. Если таблица должна обеспечивать перемещение столбцов про­
граммным способом, необходимо создать новую модель столбцов. T a b l e C o l u m n ­
Model также управляет выбором одного или нескольких столбцов. Подробно объ­
ект T a b l e C o l u m n M o d e l описан в разделе документации на API, посвященном
j avax.swing.table.

public L i s t S e l e c t i o n M o d e l g e t S e l e c t i o n M o d e l ( )
public void s e t S e l e c t i o n M o d e l ( L i s t S e l e c t i o n M o d e l s e l e c t i o n M o d e l )
Метод g e t S e l e c t i o n M o d e l возвращает, a метод s e t S e l e c t i o n M o d e l устанавливает
модель выбора. Объект L i s t S e l e c t i o n M o d e l используется как J T a b l e , так и J L i s t и
поддерживает выбор одного элемента, выбор в одном и в нескольких интервалах. При
изменении выбора ячеек объект L i s t S e l e c t i o n E v e n t помещается в очередь собы­
тий и в дальнейшем передается методу v a l u e C h a n g e d таблицы. Для определения вы­
бранных строк используются методы g e t S e l e c t e d R o w и g e t S e l e c t e d R o w s . Модель
TableColumnModel может поддерживать L i s t S e l e c t i o n M o d e l для выбора столб­
цов. Дополнительную информацию о L i s t S e l e c t i o n M o d e l вы найдете в разделе до­
кументации по API, посвященном j a v a x . swing.

public int getRowHeightO


public void s e t R o w H e i g h t ( i n t height)
public int g e t R o w H e i g h t ( i n t row)
public void s e t R o w H e i g h t ( i n t row, int height)
Первые два из указанных методов позволяют определить или установить высоту
столбца в пикселях. Высота столбца по умолчанию составляет 16 пикселей. В JDK
1.3 добавлены методы, которые дают возможность определить или установить вы­
соту одного указанного столбца.
15.4. JTable 637

public int getRowMarginO


public void setRowMargin(int margin)
Метод getRowMargin возвращает, a метод setRowMargin устанавливает расстоя­
ние в пикселях между строками таблицы. По умолчанию данное расстояние при­
нимается равным одному пикселю. Расстояние между столбцами устанавливается
посредством метода setColumnMargin объекта TableColumnModel.

public void setShowGrid(boolean show)


public boolean getShowHorizontalLines()
public void setShowHorizontalLines(boolean show)
public boolean getShowVerticalLines()
public void setShowVerticalLines(boolean show)
Метод setShowGrid разрешает или запрещает отображение горизонтальных и
вертикальных разделительных линий таблицы. Цвет линий определяется с помо­
щью установленного UI-представителя. Остальные четыре метода позволяют раз­
решить или запретить отображение горизонтальных или вертикальных линий в
таблице.
Код, представленный в листинге 15.14, создает простой объект JTable, содержа­
щий 4 столбца и 15 строк. Высота строки определяется отображаемым в ней текстом,
а также текущим шрифтом. Ширина столбцов определяется шириной области, в ко­
торой отображается таблица. Доступное пространство делится поровну между че­
тырьмя столбцами. Внешний вид объекта J T a b l e показан на рис. 15.9.

Листинг 15.14. JTableSimpleExample . Java

import j a v a . a w t . * ;
import j a v a x . s w i n g . * ;
/** Простой пример JTable, в котором заголовки таблицы и
^ данные задаются как массивы S t r i n g
Ч
public c l a s s JTableSimpleExample extends JFrame {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
new JTableSimpleExample0;

p r i v a t e f i n a l i n t COLUMNS = 4;
p r i v a t e f i n a l i n t ROWS = 15;
p r i v a t e JTcuDle sample JTable;
p u b l i c JTableSimpleExample() {
s u p e r ( " C r e a t i n g a Simple J T a b l e " ) ;
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new E x i t L i s t e n e r ( ) ) ;
Container content = g e t C o n t e n t P a n e ( ) ;
S t r i n g [ ] columnNames = buildColumnNames (COLUMNS)
638 Глава 15. Расширенные средства Swing

string[][] tableCells = buildTableCells(ROWS, COLUMNS);

sampleJTable = new JTable(tableCells, columnNames);


JScrollPane tablePane = new JScrollPane(sampleJTable);
content.add(tablePane, BorderLayout.CENTER);
setSize(450,150);
setVisible(true);
}

private String[] buildColumnNames(int columns) {


String[] header = new String[columns];
for(int i=0; i<columns; i++) {
header[i] = "Column " + i;

return(header)
}

p r i v a t e S t r i n g [ ] [ ] b u i l d T a b l e C e l l s ( i n t rows, i n t columns) {
S t r i n g [ ] [ ] c e l l s = new S t r i n g [ r o w s ] [ c o l u m n s ] ;
f o r ( i n t i=0; i<rows ; i++) {
f o r ( i n t j=0; j<columns; j++ ) {
c e l l s [i] [j] = "Row " + i + ", Col " + j ;
}
}
return(cells);

1 СоЫтп Э шш\
1 ОоШтл 0
JRowO, Col 0
1 Column 1
RowO. Col 1
1 Cs^lunw ^
RowO. Col 2 RowO, Col 3 1MI
|Row1,ColO R o w l , Col 1 Row 1. Col 2 R o w l . Col 3
IROW 2, Col 0 Row 2, Col 1 Row 2, Col 2 Row 2, Col 3
|Row3, ColO Row 3. C o l l Row3. Col 2 Row 3, Col 3 "-; 1
^Row4, Col 0 Row 4, Col 1 Row 4, Col 2 Row 4, Col 3
I R O W S , Col 0 Row 5, Col 1 Row 5, Col 2 Row 5, Col 3
iRow 6. Col 0 Row 6. Col 1 Row 6, Col 2 Row 6, Col 3
|Row7, Col 0 Row 7. Col 1 Row 7. Col 2 Row 7, Col 3
, Рис. 15.9. Простой объект JTable,
lRow8, Col 0 Row 8. C O M Row 8. Col 2 Row 8, Col 3
^ помещенный в панель с прокруткой

Модели данных, используемые компонентом


JTable
При отображении компонента JTable данные, содержащиеся в модели, выводят­
ся в ячейках таблицы. Для того чтобы данные отображались корректно, объект моде­
ли данных должен реализовывать интерфейс TableModel. В этом интерфейсе объяв­
лены методы для определения числа строк и столбцов модели, для изменения значе­
ний данных, а также для связывания с компонентом обработчиков событий и их
удаления. Большинство программистов непосредственно не реализуют интерфейс
TableModel, а предпочитают использовать класс A b s t r a c t T a b l e M o d e l , реализую­
щий этот интерфейс. Класс A b s t r a c t T a b l e M o d e l также поддерживает обработчики
событий и методы, предназначенные для генерации TableModelEvent. Для того
чтобы использовать на практике средства класса A b s t r a c t T a b l e M o d e l , необходимо
реализовать следующие методы:
15.4. JTable 639

• p u b l i c i n t getRowCount()
• p u b l i c i n t getColumnCount()
• p u b l i c Object g e t V a l u e A t ( i n t row, i n t column)
Особенности реализации данных методов определяются структурой модели данных.
В классе A b s t r a c t T a b l e M o d e l содержится реализация метода setValueAt для
поддержки нередактируемых структур данных. Поскольку s e t V a l u e A t не модифи­
цирует данные, метод i s C e l l E d i t a b l e всегда возвращает значение f a l s e .
Swing API также содержит класс DefaultTableModel, который является подклас­
сом класса A b s t r a c t T a b l e M o d e l и реализует все абстрактные методы своего супер­
класса. Данные в классе DefaultTableModel организованы в виде объектов Vector.
Элементами вектора строк также являются объекты Vector, содержащие данные
ячеек. Для того чтобы соответствовать структуре данных, массивы, передаваемые
конструктору DefaultTableModel, преобразуются в объекты Vector. Если таблица
имеет большие размеры, инициализация объекта DefaultTableModel происходит
достаточно долго. Метод i s C e l l E d i t a b l e класса DefaultTableModel всегда воз­
вращает значение t r u e — это значит, что пользователь может выбрать любую ячейку
таблицы и модифицировать ее содержимое.
На заметку
Метод IsCellEditable класса AbstractTableModel всегда возвра­
щает значение false; в классе DefaultTableModel этот метод воз­
вращает значение tJTue.

Конструктор DefaultTableModel
в классе DefaultTableModel определено шесть конструкторов. Три из них, ис­
пользуемые наиболее часто, описаны ниже.

public DefaultTableModel(Object[][] data, Object[] columnNames)


Данный конструктор создает объект DefaultTableModel, которому содержимое
ячеек таблицы передается в виде двухмерного массива d a t a ; кроме того, одномер­
ный массив columnNames определяет имена столбцов. Иногда данные ячеек и
имена столбцов представляются объектами S t r i n g . При наличии соответствую­
щих средств визуализации могут быть также использованы другие объекты
(изображения, флажки опций и др.)-

public DefaultTableModel(Vector data, Vector columnNames)


Указанному конструктору данные передаются в виде объектов Vector. Каждый
элемент вектора d a t a представляет собой строку таблицы и также является век­
тором, элементы которого определяют отдельные ячейки. Вектор columnNames
задает имена столбцов.

public DefaultTableModel(Vector columnNames, int numRows)


Данный конструктор создает объект DefaultTableModel с заданными именами
столбцов и заданным количеством строк. Каждая строка таблицы представляет
640 Глава 15. Расширенные средства Swing

собой вектор, размер которого равен числу столбцов. Первоначально вместо дан­
ных таблицы используются значения n u l l .

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


Класс D e f a u l t T a b l e M o d e l содержит методы, позволяющие добавлять к модели
данных строки и столбцы и удалять их, а также изменять содержимое ячеек. Наибо­
лее часто используемые методы описаны ниже.

public void a d d C o l u m n ( O b j e c t c o l u m n N a m e )
public v o i d a d d C o l u m n ( O b j e c t c o l u m n N a m e , Object[] columnData)
public void a d d C o l u m n ( O b j e c t c o l u m n N a m e , Vector columnData)
Первый из указанных методов добавляет столбец с указанным именем к модели
таблицы. Ячейки нового столбца заполняются значениями n u l l . Два других мето­
да также добавляют столбец к модели таблицы, но они позволяют задавать данные
для ячеек столбца в виде массива либо вектора.

public void addRow(Object[] rowData)


public void addRow(Vector rowData)
public void insertRow(int row, Object[] rowData)
public void insertRow(int row. Vector rowData)
Методы addRow добавляют строку к модели; данные задаются в виде массива либо
в виде вектора. Новая строка включается за последней строкой модели данных.
При использовании методов I n s e r t R o w указывается строка, в которую включают­
ся данные. Если в качестве параметра r o w D a t a передается n u l l , cipoKa заполня­
ется значениями n u l l . Если при вызове данных методов указывается строка за
пределами диапазона, определенного в модели, генерируется исключение А г г а у -
IndexOutOfBoundsException.

public void removeRow(int row)


Метод r e m o v e Row удаляет указанную строку из модели таблицы. Если номер стро­
ки меньше нуля либо превышает число строк, определенных в модели, генериру­
ется исключение A r r a y l n d e x O u t O f B o u n d s E x c e p t i o n .

public int getColumnCount()


public int getRowCount()
Метод g e t C o l u m n C o u n t возвраш[ает число столбцов, a метод g e t R o w C o u n t воз­
вращает число строк в модели таблицы.

public String g e t C o l u m n N a m e ( i n t c o l u m n )
Данный метод возвращает имя указанного столбца, полученное в результате при­
менения метода t o S t r i n g . Если вместо имени столбца задано значение n u l l , то
возвращаются символы, соответствующие соглашению об именовании столбцов
(А, В, С, ..., АА, ВВ, СС, ...). Если заданный номер столбца меньше нуля, а также ее-
15,4. JTable 641

ли он равен или превышает число столбцов, определенных в модели, генерирует­


ся исключение A r r a y l n d e x O u t O f B o u n d s E x c e p t i o n .

public Object getValueAt(int row, int c o l u m n ) public


setValueAt(Object value, int row, int c o l u m n )
Метод g e t V a l u e A t возвраи^ает, a метод s e t V a l u e A t устанавливает содержимое
ячейки, определяемой номером строки и столбца. После изменения данных в
ячейке метод s e t V a l u e A t генерирует событие T a b l e M o d e l E v e n t , предназна­
ченное для обновления внешнего вида таблицы. Если заданный номер строки ли­
бо столбца лежит за пределами допустимого диапазона, генерируется исключение
ArraylndexOutOfBoundsException.

public b o o l e a n isCellEditable(int row, int c o l u m n )


Для объекта Def a u l t T a b l e M o d e l метод i s C e l l E d i t a b l e всегда возвращает зна­
чение t r u e , независимо от значения строки и столбца. Каждая ячейка в Def a u l t ­
T a b l e M o d e l допускает изменение содержимого.
В листинге 15.15 приведен пример использования класса Def a u l t T a b l e M o d e l . Для
представления данных применяется класс J a v a L o c a t i o n C o l l e c t i o n , код которого
был приведен в листинге 15.5. Каждый объект J a v a L o c a t i o n содержит следующую
информацию, связанную с регионом, с названием "Java": название страны, в которой
находится этот регион, флаг страны (GIF-файл) и комментарии с описанием городов,
находящихся по соседству. В данном примере сначала создается "пустой" объект
Def a u l t T a b l e M o d e l ; в дальнейшем к нему посредством метода addColumn добавляют­
ся столбцы. После добавления столбцов к модели таблицы последовательно добавляют­
ся строки; для этого вызывается метод getRowData, который строит вектор на базе
информации, содержащейся в J a v a L o c a t i o n , а затем вызывается метод addRow, до­
бавляющий новую строку в таблицу. Помимо информации о городе и стране, в таблице
предусмотрен столбец для представления флага страны ( I m a g e I c o n ) и столбец, содер­
жащий логическое значение, определяющее, посещал ли пользователь этот город.
Результаты выполнения данного примера показаны на рис. 15.10. Заметьте, что
каждая ячейка содержит представление соответствующего объекта в виде строки.
В следующем разделе мы покажем, как использовать встроенные средства визуализа­
ции для отображения объектов Image I c o n в виде пиктограмм и объектов B o o l e a n в
виде флажков опций.

Листинг 1 5 . 1 5 . D e f a u l t T a b l e E x a m p l e . J a v a

import Java.util.Vector;
import javax.swing.*;
import javax.swing.table.*;

/** J T a b l e , использующий D e f a u l t T a b l e M o d e l и позволяющий


* д о б а в л я т ь с т р о к и и столбцы из программы.
V

p u b l i c c l a s s D e f a u l t T a b l e E x a m p l e e x t e n d s JTable {
642 Глава 15. Расширенные средства Swing

private String[] columnNames =


{ "Flag", "City", "Country", "Comment", "Visited" };

piiblic DefaultTableExample () {
this(new DefaultTableModelО);

public DefaultTableExample(DefaultTableModel model) {


super(model);

JavaLocationCollection collection =
new JavaLocationCollection0;
JavaLocation[] locations = collection.getLocations();

// Установка заголовков столбцов и данных для модели таблицы.

int i;
for(i=0; i<columnNames.length; i++ ) {
model.addColumn(columnNames[i]);
}
for(i=0; i<locations.length; i++) {
model.addRow(getRowData(locations[i]));
}
}

private Vector getRowData(JavaLocation location) {


Vector vector = new Vector();
' vector.add(new Imagelcon(location.getFlagFile()) ) ;
vector.add("Java");
vector.add(location.getCountry());
vector.add(location.getComment());
vector.add(new Boolean(false));
return(vector) ;
}

p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
WindowUtilities.setNativeLookAndFeel();
WindowUtilities.openlnJFrame(
new JScrollPane(new D e f a u l t T a b l e E x a m p l e ( ) ) , 600, 150,
"Using a DefaultTableModel");
}

l o u s i n g a OefauRTableM idel -:ттШШш1


; Flag 1 cm 1 Coun^ 1 Comment
1false vi'stted У 1
;flags/beigium gif Java Belgium near Liege j4
Iflags/brazil gif Java Brazil near Salvador false
[flags/colombia gif Java Colombia near Bogota false
Itlags/indonesia gif Java Indonesia mam island false Рис. 15.10. Таблица,
Iflags/jamaica gif Java Jamaica near Spanish Town false ^
;flags/tnorambiqcie Java Mozambique nearSofala false построенная с исполк>зованием
flags/philippines gif Java Philippines near Quezon City false
_ Ji модели DefaultTableModel
15.4. JTable 643

Визуализация ячеек таблицы


Обычно для представления содержимого ячеек таблицы используются компонен­
ты J L a b e l . Однако для некоторых классов Java 2 Platform предоставляет дополни­
тельные средства визуализации, позволяющие отображать объекты в более подходя­
щем для них формате. В настоящее время существуют средства визуализации для сле­
дующих типов объектов.

• B o o l e a n — отображается посредством компонента J C h e c k B o x .


• D a t e — отображается с помощью компонента J L a b e l , но перед этим выполня­
ется форматирование средствами класса D a t e F o r m a t .
• Image I c o n — выводится как изображение в составе J L a b e l .
• Number— после форматирования данных с помощью класса N u m b e r F o r m a t
отображается как J L a b e l .
• Ob j е с t — после вызова метода t o S t r i n g отображается как J L a b e 1.
Для того чтобы воспользоваться средствами визуализации, надо переопределить ме­
тод g e t C o l u m n C l a s s класса D e f a u l t T a b l e M o d e l так, чтобы он предоставлял инфор­
мацию о типе данных, содержащихся в каждом столбце. По умолчанию g e t C o l u m n ­
C l a s s возвращает значение O b j e c t . c l a s s , что приводит к отображению объекта по­
средством J L a b e l .
Как правило, для обеспечения взаимодействия со средствами визуализации, ис­
пользуемыми по умолчанию, строят новую модель таблицы, создавая подкласс класса
D e f a u l t T a b l e M o d e l . В этом подклассе переопределяют метод g e t C o l u m n C l a s s так,
чтобы он возвращал реальный тип объекта. Например:
p u b l i c C l a s s g e t C o l u m n C l a s s ( i n t column) {
return (getValueAt(0, column).getClass());
}
В приведенном фрагменте кода метод возвращает класс объекта, находящегося в
первой ячейке указанного столбца. Такой подход можно использовать в предположе­
нии, что столбец содержит объекты одного типа или, по крайней мере, что все объ­
екты столбца имеют общий суперкласс, который может возвращаться методом
g e t C o l u m n C l a s s . Средства визуализации для этого общего суперкласса должны быть
установлены в таблице.
В листинге 15.16 показан код класса C u s t o m T a b l e E x a m p l e , который является
подклассом рассмотренного раньше класса D e f a u l t T a b l e E x a m p l e . В классе
C u s t o m T a b l e E x a m p l e определен новый конструктор, в теле которого создается эк­
земпляр класса C u s t o m T a b l e M o d e l (листинг 15.17) как модель данных для таблицы.
Класс C u s t o m T a b l e M o d e l является подклассом D e f a u l t T a b l e M o d e l и в нем пере­
определен метод g e t C o l u m n C l a s s . Кроме того, в классе C u s t o m T a b l e M o d e l пере­
определен метод i s C e l l E d i t a b l e ; это сделано для того, чтобы ограничить возмож­
ности пользователя по модификации данных столбцами Comment и V i s i t e d . Резуль­
таты выполнения программы показаны на рис. 15.11.
Чтобы скорректировать внешний вид таблицы, в программе явно задается ширина
каждого столбца, для чего используются методы s e t M i n W i d t h , s e t M a x W i d t h и
s e t P r e f e r r e d W i d t h . П о умолчанию нормальная ширина столбца принята равной
644 Гл1ава 15. Расширенные средства Swing

75 пикселям, а минимальная — 15 пикселям. Ш и р и н а столбца устанавливается с по­


мощью кода, подобному следующему:

TableColumn column = table.getColumn(columnName);


column.setPreferredWidth(numPixels);

В некоторых реализациях J D K допущена ошибка. Чтобы устранить ее влияние на


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

table.sizeColumnsToFit(JTable.AUTO_RESIZE_OFF);
В классе TableColumn также определен метод setWidth, однако ширина столбца,
установленная с его помощью, не сохраняется после изменения размеров таблицы.

Листинг 15.16.CustomTableExample.Java

import javax.swing.*;
import javax.swing.table.*;

/** JTable, использующий CustomTableModel для


* корректного воспроизведения ячеек таблицы, содержащих
* изображения и двоичные значения.
V
public class CustomTableExample extends DefaultTableExample {

public CustomTableExample() {
super(new CustomTableModel());
setCellSizes();
}
private void setCellSizes () {
setRowHeight(50);
getColumn("Flag").setMaxWidth(55);
getColumn("City").setPreferredWidth(60);
getColumn("Country").setMinWidth(80);
getColumn("Comment").setMinWidth(150);
// Вызов метода для повторной установки размеров
// столбцов (коррекция ошибки).
SizeColumnsToFit(JTable.AUTO_RESIZE_OFF);
}
public static void main(String[] args) {
WindowUtilities.setNativeLookAndFeel();
WindowUtilities.openlnJFrame(
new JScrollPane(new CustomTableExample()), 525, 255,
"Using a CustomTableModel");
}
15.4.JTable 645

Листинг 15.17. CustomTableModel. Java

import javax.swing.table.*;

/** Модифицированный класс DefaultTableModel, метод которого


* возвращает тип для средств визуализации ячейки,
* используемых по умолчанию. Возможности пользователя по
'^ редактированию ограничены столбцами Comment и Visited.
V
public class CustomTableModel extends DefaultTableModel {

public Class getColumnClass(int со1глап) {


return(getValueAt(0, column).getClass());
}
// Разрешение редактирования столбцов "Comment" и "Visited",
public boolean isCellEditable(int row, int column) {
return(column==3 || column==4);

Usma a {JmAimTeMeiiodel
Fiag Crty Coia^ I Cemment
m
11 1 Java

1 Java
Belgium

Braz.l
inear Liege

inear Salvador
@

E J Java Colombia Inear Bogota


Рис. 15.11. Использование средств

E 4;Java Indonesia imain island


_rJ
визуализации для отображения
пиктограмм и флажков опций

Обработка событий таблицы


События таблицы не обрабатываются непосредственно объектами прослушива­
ния, связанными с компонентом J T a b l e . Вместо этого обработчики связываются с
одной или несколькими моделями таблицы: T a b l e M o d e l , T a b l e C o l u m n M o d e l или
L i s t S e l e c t i o n M o d e l . Для обработки событий, имеющих отношение к содержимому
ячеек, объект T a b l e M o d e l L i s t e n e r подключается к модели таблицы T a b l e M o d e l . В
интерфейсе T a b l e M o d e l L i s t e n e r объявлен единственный метод t a b l e C h a n g e d ,
который вызывается при изменении данных в таблице (когда пользователь нажимает
клавишу <Enter> или когда ячейка теряет фокус ввода). Данному методу передается
объект T a b l e M o d e l E v e n t . Чтобы определить, какая из ячеек таблицы стала причи­
ной возникновения события, надо воспользоваться методами g e t C o l u m n , а также
g e t F i r s t R o w или g e t L a s t R o w . Заметьте, что в объекте D e f a u l t T a b l e M o d e l метод
i s C e l l E d i t a b l e всегда возвраш,ает значение t r u e , поэтому каждая ячейка может
быть модифицирована и средства редактирования, используемые по умолчанию, ин­
терпретируют содержимое ячейки как текст.
646 Глава 15. Расширенные средства Swing

Когда пользователь нажимает клавишу <Enter> или переходит к другой ячейке


таблицы, генерируется событие T a b l e M o d e l E v e n t . Для обработки этого события
надо связать с T a b l e M o d e l объект T a b l e M o d e l L i s t e n e r . Это можно сделать с по­
мощью следующего фрагмента кода:
tablemodel.addTableModelListener(
new TableModelListner() {
public void tableChanged(TableModelEvent event) {
int row = event.getFirstRow0;
int column = event.getColumn0;

}
});
Выяснив, какая из ячеек стала причиной возникновения события, вы можете по­
лучить содержимое ячейки, вызвав метод g e t V a l u e A t ( r o w , c o l u m n ) , либо задать
новые данные посредством метода s e t V a l u e A t ( r o w , c o l u m n ) . Метод g e t V a l u e A t
возвращает значение O b j e c t ; это значение надо привести к соответствующему типу.
Реально изменения данных проводятся в рамках модели, лежащей в основе таблицы.
Поэтому для обновления внешнего вида объекта J T a b l e надо сгенерировать событие
TableModelEvent.
Класс A b s t r a c t T a b l e M o d e l , являющийся суперклассом для T a b l e M o d e l , предос­
тавляет семь методов, предназначенных для генерации событий. Чаще всего исполь­
зуются два метода: f i r e T a b l e C e l l U p d a t e d ( r o w , c o l u m n ) , который обновляет
конкретную ячейку, и f i r e T a b l e D a t a C h a n g e d () , который обновляет всю таблицу.
Java 2 Platform также определяет события T a b l e C o l u m n M o d e l E v e n t для внесения
изменений в T a b l e C o l u m n M o d e l и L i s t S e l e c t i o n E v e n t — для внесения изменений
в ListSelectionModel.
В листинге 15.18 показан код, создающий таблицу, в которой пользователь указы­
вает книги, которые он собирается приобрести. В программе импортируется пакет
j a v a x . s w i n g . e v e n t и обработчик событий связывается с моделью таблицы. Когда
пользователь изменяет число книг, объект T a b l e M o d e l L i s t e n e r получает событие
T a b l e M o d e l E v e n t и вычисляет общую стоимость заказа. По окончании вычислений
и записи данных в модель генерируется событие T a b l e M o d e l E v e n t , предназначен­
ное для обновления представления данных на экране. Результаты выполнения про­
граммы показаны на рис. 15.12.
Значительная часть кода в составе обработчика преобразовывает данные из стро­
кового формата в числовой и наоборот. Если вы незнакомы с классом
D e c i m a l F o r m a t , прочитайте о нем в разделе документации по API, посвященном
j ava.text.DecimalFormat.

Листинг 1 5 . 1 8 . J T a b l e E v e n t s o a v a

import java.awt.*;
import Java.text.DecimalFormat;
import javax.swing.*;
import j avax.swing.event.*;
import javax.swing.table.*;

/** Компонент JTable, который реагирует на события


15.4. JTable 647

* TableModelEvent и обновляет ячейки таблицы на основе


* данных, введенных пользователем.
V
public class JTableEvents extends JFrame {
private final int COL_COST = 1;
private final int COL_QTY = 2;
private final, int COL_TOTAL = 3;
private final int ROW_LAST = 5;
private DecimalFormat df = new DecimalFormat("$####.##");
private JTable sampleJTable;
private DefaultTableModel tableModel;

public static void main(String[] args) {


new JTableEvents();
}
public JTableEvents() {
super("Using TableEvents");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();

String[] columnNames = { "Book", "Cost", "Qty", "Total" };

final Object[][] data = {


{"Core Web Programming", "$ 0.99", "0", "$0.00"},
{"Core Servlets and JavaServer Pages",
"$34.39", "0", "$0.00"},
{"Core Swing", "$39.99", "0", "$0.00"},
{"Core Java, Volume I", "$31.49", "0", "$0.00"},
{"Core Java, Volume II", "$34.39", "0", "$0.00"},
{null, null, "Grand:", "$0.00"} };
tcdDleModel = new DefaultTableModel (data, columnNames);
tcibleModel. addTableModelListener (
new TableModelListener0 {
int row, col;
int quantity;
float cost, subTotal, grandTotal;

public void tableChanged(TcUDleModelEvent event) {


row = event.getFirstRowO ;
col = event.getColumn();
// Обновление таблицы при вводе нового количества книг,
if (col == COL_QTY) {
try {
cost = getFormattedCellValue(row, COL_COST);
quantity = (int)getFormattedCellValue(row, COL_QTY);
subTotal = quantity * cost;

// Обновление строки.
tableModel.setValueAt(df.format(subTotal),
row, COL_TOTAL);
// Полное обновление.
grandTotal =0;
for(int row=0; row<data.length-1; row++) {
648 Глава 15. Расширенные средства Swing

grandTotal += getFormattedCellValue(row, COL TOTAL)


}
tableModel.setValueAt(df.format(grandTotal),
ROW__LAST, COL_TOTAL) /
tableModel.fireTableDataChanged()/
} catch (NumberFormatException nfe) {
// Передача пользователю сообщения об ошибке.
JOptionPane,showMessageDialog(
JTableEvents.this,
"Illegal value entered!");
}

private float getFormattedCellValue(int row, int col) {


String value = (String)tableModel.getValueAt(row, col)
return(Float.parseFloat(value.replace('$',' ')));
}
});
sampleJTable = new JTable(tableModel);
setColumnAlignment(sampleJTable.getColumnModel());
JScrollPane tablePane = new JScrollPane(sampleJTable);

content.add(tablePane, BorderLayout.CENTER);
setSize(460,150);
setVisible(true);

// Bee столбцы, за исключением первого,


// выравниваются по правому краю.
private void setColumnAlignment(TableColumnModel tcm) {
TableColumn column;
DefaultTableCellRenderer renderer =
new DefaultTableCellRenderer*();
for(int i=l; i<tcm. getColumnCount () ; i+-b) {
column = tcm.getColumn(i);
renderer.setHorizontalAlignment(SwingConstants.RIGHT);
column.setCellRenderer(renderer);
}
}

ЩUsing TdbleEvenU

•Core Servlets and JavaSetver Pages $34 39 $10317


:Core Swing $39 99 $39 99
Core Java. Volume 1 $31 49 $31 49
iCore Java, Volume II $34 39 $34 39 Рис. 15.12. Таблица, в которой вычисляется
$213 99
общая стоимость заказанных книг
15.5. Вывод на печать Swing-компонентов 649

15.5. Вывод на печать Swing-компонентов


Платформа Java 2 обеспечивает высококачественную печать с помощью классов, со­
держащихся в пакете J a v a . a w t . p r i n t . В данном разделе описывается использование
данного пакета для вывода Swing-компонентов на принтер. Вывод на печать не сводится
в отображению компонентов посредством объекта G r a p h i c s 2 D с последующим копи­
рованием полученного изображения на принтер. Работая с JDK 1.2, вам придется перед
отображением компонента полностью отключить двойную буферизацию. В JDK 1.3 мо­
дель печати претерпела изменения и для поддержки двойной буферизации к классу
JComponent был добавлен метод p r i n t . В данном разделе мы рассмотрим основы вы­
вода на печать и покажем, почему важно отключать двойную буферизацию при работе в
JDK 1.2. Затем мы продемонстрируем вывод на печать B J D K 1.3.

Основы вывода на печать


Процедура вывода на п р и н т е р состоит из двух этапов: установки задачи печати и
отображения графических данных на принтере.

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


Установка задачи вывода на печать всегда происходит одинаково. Для этого надо
получить объект P r i n t e r J o b , передать объект P r i n t a b l e методу s e t P r i n t a b l e ,
вызвать p r i n t D i a l o g , чтобы отобразить диалоговое окно, специфическое для ис­
пользуемой операционной системы, и вызвать метод p r i n t объекта P r i n t e r J o b .
Диалоговое окно печати в системе Windows 98 показано на рис. 15.13. Учитывая, что
пользователь может закрыть окно, щелкнув мышью на кнопке Cancel, вы должны пе­
ред вызовом p r i n t проверить значение, возвращаемое методом p r i n t D i a l o g .

Pfinter
••••EQI
Najr.e Eroperties 1
Шшззшвшшшшшт• H d „
Status Ready
Type; H P LaserJet 4000 Senes РГз

Where- 128.244128 247.RAW


Comment. ИР 4000 If! Stev/e Dtan.r.r-. i, -,(, •• ? 51
'
Print range

(^ Ah f' "^
f" Page 6 tiorrr p to 1 ' •
r Collate

1"" OK ^ Cancel j 1

1
Рис. 15.13. Внешний вид окна, отображаемого в результа­
те вызова метода p r i n t D i a l o g
650 Глава 15. Расширенные средства Swing

Фрагмент кода, с помощью которого устанавливается задача вывода на печать, вы­


глядит следующим образом:
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(this);
if (printJob.printDialog0)
try {
printJob.print();
} c a t c h ( P r i n t e r E x c e p t i o n pe) {
System.out.println("Error printing: " + pe);
}

Отображение графических данных на принтере


в объекте P r i n t a b l e , передаваемом методу s e t P r i n t a b l e , должен быть реали­
зован метод p r i n t , который описывает особенности передачи изображения на
принтер. При вызове метода p r i n t ему передаются три параметра: объекты
Graphics и PageFormat, а также целочисленное значение page Index. Метод p r i n t
вызывается из объекта P r i n t e r Job и предназначен для представления графического
объекта в соответствии с заданным форматом страницы. Параметр page Index задает
номер страницы документа. Вначале P r i n t e r Job вызывает метод p r i n t и передает
значение page Index, равное 0. Если данная страница уже выведена на печать, метод
должен вернуть значение PAGEEXISTS. Значение N0 SUCH_PAGE возвращается в
случае, если страниц для вывода больше нет.
Java многократно вызывает метод p r i n t , увеличивая при каждом вызове счетчик
страниц; это продолжается до тех пор, пока метод не вернет значение
NO_SUCH_PAGE. Поэтому организуя вывод на печать, вы должны сначала решить, ка­
кие страницы должны выводиться. При выводе окна с графическим пользователь­
ским интерфейсом весь документ состоит из одной страницы, поэтому для страницы
с номером О вы должны возвращать значение PAGEEXISTS, а для остальных стра­
ниц - NO_SUCH_PAGE.
После того как вы приняли решение о выводе страниц, можно приступать к созда­
нию средств, предназначенных для рисования. Для Swing-компонентов информация,
выводимая на печать, должна повторять внешний вид экрана, но отображаться с вы­
соким разрешением. Для выполнения рисования надо преобразовать объект
Graphics к типу Graphics2D, изменить разрешение объекта в соответствии с раз­
решающей способностью принтера и вызвать метод p a i n t компонента, передавая
ему объект Graphics2D с установленным разрешением. Подробно класс Graphics2D
был описан в главе 10.
Фрагмент кода, выполняющий вывод на печать, приведен ниже.
public int print(Graphics g,
PageFormat pageFormat,
int pagelndex) {
if (pagelndex > 0) {
return(NO_SUCH_PAGE);
} else {
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pageFormat.getlmageableX{),
pageForitiat .getlmageableY () ) ;
15.5. Вывод на печать Swing-компонентов 651

// при использовании JDK 1.вам необходимо


// перед выводом компонента на печать отключить
// двойную буферизацию, а после окончания печати
// включить ее снова. В JDK 1.3 приведенные ниже
// три строки заменяются следующим вызовом:
// componentToBePainted.print(g2d).
setDoubleBufferEnabled(false);
componentToBePrinted.paint(g2d);
setDoubleBufferEnabled(true);

return(PAGE EXISTS);
}
}
Методы g e t l m a g e a b l e X и g e t l m a g e a b l e Y возвращают координаты левого верх­
него угла изображения, выводимого на печать. Оно определяется исходя из типа
принтера, размера и ориентации бумаги, заданных в диалоговом окне Printer.

Двойная буферизация и вывод на печать


Для Swing-компонентов двойная буферизация по умолчанию включена. В боль­
шинстве случаев использование двойной буферизации предоставляет разработчику
большие преимущества, позволяя использовать удобный и э ф ф е к т и в н ы й метод
p a i n t C o m p o n e n t . Однако при выводе на принтер двойная буферизация может стать
источником проблем. Дело в том, что печать не сводится к масштабированию содер­
жимого внеэкранного буфера. П р и выводе на принтер происходит преобразование
системы координат, после чего вызывается метод p a i n t компонента. Масштабиро­
вание внеэкранного буфера приведет к генерации файла спулинга большого размера,
необходимость обработки которого приведет к замедлению вывода на печать.
Таким образом, перед выводом на принтер Swing-компонентов необходимо убе­
диться в том, что двойная буферизация отключена. Если вы работаете с одним ком­
понентом, например с панелью J P a n e l , вы можете перед обращением к методу
p a i n t вызвать s e t D o u b l e B u f f a r e d ( f a l s e ) , а после завершения работы метода вы­
звать s e t D o u b l e B u f f a r e d ( t r u e ) . Однако если вы впоследствии включите в состав
панели другой контейнер, то для него двойная буферизация будет по умолчанию
включена и вы вернетесь к тому, с чего начали. Гораздо лучше полностью отключить
двойную буферизацию. Сделать это можно следующим образом:
RepaintManager currentManager =
RepaintManager.currentManager(theComponent);
currentManager.setDoubleBufferingEnabled(false);
После вызова метода p a i n t двойную буферизацию можно снова разрешить п)пгем
вызова D o u b l a B u f f e r i n g E n a b l e d ( t r u e ) . Данный подход полностью решает про­
блемы с выводом простых компонентов на принтеры со сравнительно низкой разре­
шающей способностью. Однако при выводе компонентов, для которых в качестве
фона используются сложные изображения, снова создаются большие файлы спулера,
что приводит к замедлению печати. В JDK 1.3 отключение двойной буферизации ав­
томатически осуществляется в процессе печати, поэтому разработчику нет необхо­
димости заботиться об этом.
652 Глава 1 5 . Р а с ш и р е н н ы е средства Swing

На з а м е т к у

При использовании JDK 1.2 надо перед выводом компонента на пе­


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

Универсальная программа печати


При выводе компонентов Swing на печать роль метода p r i n t сводится к масшта­
бированию объекта G r a p h i c s , отключению двойной буферизации и последующему
ее включению. Однако совершенно не обязательно, чтобы метод p r i n t принадлежал
компоненту, предназначенному для вывода на принтер. Более того, создание компо­
нентов, реализующих интерфейс P r i n t a b l e , и определение в них метода p r i n t
приводит к созданию громоздкого кода и затрудняет работу с компонентами, вывод
на печать которых первоначально не планировался. Гораздо удобнее реализовать ме­
тод p r i n t в отдельном объекте и указать этому объекту компоненты, для которых
должны быть вызваны методы p a i n t . Такой подход позволят создать универсальный
метод p r i n t C o m p o n e n t , которому в качестве параметра передается компонент, пред­
назначенный для печати.

М е т о д и к а профессионалов

Вместо того чтобы модифицировать код каждого Swing-компонента,


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

В листинге 15.19 представлен код вспомогательного класса P r i n t U t i l i t i e s ,


предназначенного для печати Swing-комопнентов. Компонент, который должен быть
выведен на принтер, надо передать метод)' P r i n t U t i l i t i e s . p r i n t C o m p o n e n t .
В листинге 15.20 приведен пример использования класса P r i n t U t i l i t i e s B J D K 1.2.

Листинг 1 5 . 1 9 . P r i n t U t i l i t i e s . J a v a

import java.awt.*;
import javax.swing.*;
import Java.awt.print.*;

/** Простой вспомогательный к л а с с , который п о з в о л я е т ,


* р а б о т а я с JDK 1 . 2 , выводить на п е ч а т ь произвольные
* компоненты. Требуемый компонент надо лишь п е р е д а т ь
* методу P r i n t U t i l i t i e s . p r i n t C o m p o n e n t . В компоненте,
* п р е д н а з н а ч е н н о м для вывода на п е ч а т ь , не о б я з а т е л ь н о
* должен п р и с у т с т в о в а т ь метод p r i n t . Этот компонент
* также не должен р е а л и з о в ы в а т ь никакие дополнительные
* интерфейсы и выполнять специфические д е й с т в и я .
* <Р>
* Если печать надо выполнять многократно, то повысить
* эффективность работы можно, если предварительно выполнить
* следующую операцию:
15.5. Вывод на печать Swing-компонентов 653

* <PRE>
* PrintUtilities printHelper =
* new PrintUtilities(theComponent);
* </PRE>
* a затем вызывать printHelper.print(). Однако
* в большинстве случаев проще обращаться к
'*' PrintUtilities . printComponent (componentToBePrinted)
V
public class PrintUtilities imiplements Printable {
protected Component componentToBePrinted;

public static void printComponent(Component c) {


new PrintUtilities(c).print();

public PrintUtilities(Component componentToBePrinted) {


this.componentToBePrinted = componentToBePrinted;
}
public void print 0 {
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(this);
if (printJob.printDialog0)
try {
printJob.print();
} catch(PrinterException pe) {
System.out.println("Error printing: " + pe);

// Процедура печати общего назначения для JDK 1.2.


// Для печати в JDK 1.3 следует использовать
// PrintUtilities2.
public int print(Graphics g, PageFormat pageFormat,
int pagelndex) {
if (pagelndex > 0) {
return(NO_SUCH_PAGE);
} else {
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pageFormat.getlmageableX(),
pageFormat.getlmageableY());
disableDoubleBuffering(componentToBePrinted);
componentToBePrinted.paint(g2d);
enableDoubleBuffering(componentToBePrinted);
return(PAGE_EXISTS);
}
}

/** Если двойная буферизация включена, скорость и


* качество печати существенно снижаются, поэтому
*• двойную буферизацию следует отключить. Сказанное
* справедливо только для JDK 1.2.
V
public static void disableDoubleBuffering(Component с) {
654 Глава 15. Расширенные средства Swing

RepaintManager currentManager =
RepaintManager.currentManager(с);
currentManager.setDoubleBufferingEnabled(false);
}
/** Возобновление двойной буферизации необходимо
* только при работе с JDK 1.2.
V
public static void enableDoubleBuffering(Component с) {
RepaintManager currentManager =
RepaintManager.currentManager(с);
currentManager.setDoubleBufferingEnabled(true);
}

В листинге 15.20 показан код приложения, обладающего возможностями печати.


В окно программы включена кнопка, при активизации которой вызывается метод
PrintUtilities .printComponent. При работе приложения используется класс
DrawingPanel (листинг 15.21), в котором реализован метод paintComponent. Этот
метод выводит текст "Java 2D" с эффектом тени. Внешний вид окна приложения пока­
зан на рис. 15.14. Более подробно о рисовании и преобразовании изображений с по­
мощью объекта Graphics2D см. в главе 10.

Листинг 15.20. PrintExample. java

import java.awt.*;
import javax.swing.*;
import Java.awt.event.*;
import Java.awt.print.*;

/** Пример окна, поддерживающего вывод на печать в Java 1.2.


* В Java 1.2 вывод на принтер допускает <В>любой</В> компонент,
* однако следует следить за тем, чтобы двойная буферизация
* была отключена глобально (не только для окна верхнего уровня)
* Метод printComponent класса PrintUtilities позволяет
* выводить на печать произвольный компонент.

public class PrintExample extends JFrame


implements ActionListener {
public static void main(String[] args) {
new PrintExample();
}
public PrintExample() {
super("Printing Swing Components in JDK 1.2");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
JButton printButton = new JButton("Print");
printButton.addActionListener(this);
JPanel buttonPanel = new JPanelO;
15.5. Вывод на печать Swing-компонентов 655

buttonPanel.setBackground(Color.white)/
buttonPanel.add(printButton);
content.add(buttonPanel, BorderLayout.SOUTH);
DrawingPanel drawingPanel = new DrawingPanel();
content.add(drawingPanel, BorderLayout.CENTER);
packO ;
setVisible(true);

public void actionPerformed(ActionEvent event)


PrintUtilities.printComponent(this);
}
}

Листинг 15.21.DrawingPanel.Java

import java.awt.*;
import javax.swing.*;
import Java.awt.geom.*;

/** Окно с переопределенным методом paintComponent.


* Данный пример показывает, что универсальный метод
* печати можно применять даже к тем компонентам, которые
* выполняют произвольные операции рисования.
* Метод printComponent класса PrintUtilities позволяет
* выводить на печать любой компонент.
V
public class DrawingPanel extends JPanel {
private int fontSize = 90;
private String message = "Java 2D";
private int messageWidth;

public DrawingPanel() {
setBackground(Color.white);
Font font = new Font("Serif", Font.PLAIN, fontSize);
setFont(font);
FontMetrics metrics = getFontMetrics(font);
messageWidth = metrics.stringWidth(message);
int width = messageWidth*5/3;
int height = fontSize*3;
setPreferredSize(new Dimension(width, height));

/•• Отображение строки с эффектом "отбрасывания тени'

public void paintComponent(Graphics g) {


super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
int X = messageWidth/10;
int у = fontSize*5/2;
g2d.translate(x, y ) ;
g2d.setPaint(Color.lightGray);
AffineTransform origTransform = g2d.getTransform();
656 Глава 1 5 . Р а с ш и р е н н ы е средства Swing

g2d.shear(-0.95, 0) ;
g2d.scale(1, 3 ) ;
g2d.drawstring(message, 0, 0 ) ;
g2d.setTransform(origTransform);
g2d.setPaint(Color.black);
g2d.drawstring(message, 0, 0) ;

l-^<Jllil'lilil'.H'!'»4liilll.H.llli!l>i4H

Java 2D Рис. 15.14. При выборе компонента JButton


вызывается метод p r i n t , который передает
объект Graphics2D на печать

Вывод на печать в JDK 1.3


в отличие O T J D K 1.2, в JDK 1.3 разработчик не должен заботиться о запрещении и
разрешении двойной буферизации. В JDK 1.3 в классе J C o m p o n e n t определен метод
p r i n t , поддерживающий вывод на печать. В методе p r i n t устанавливается внутрен­
ний флаг IS_PRINTING и вызывается метод p a i n t . Метод p a i n t класса J C o m p o n e n t
переопределен так, что если флаг установлен, то перед вызовом методов p r i n t -
Component, p r i n t B o r d e r и p r i n t C h i l d r e n , определенных как p r o t e c t e d , двойная
буферизация запрещается.
При работе с JDK 1.3 вместо набора команд
setDoubleBufferEnabled(false);
componentToBePrinted.paint(g2d);
setDoubleBufferEnabled(true);
используется выражение
componentToBePrinted.print(g2d);

М е т о д и к а профессионалов

Вывод на печать в JDK 1.3 осуществляется с помощью метода


print (Graph±cs2d g2d). При работе с JDK 1.3 больше нет необхо­
димости запрещать и разрешать двойную буферизацию.

В листинге 15.22 показан код нового вспомогательного класса P r i n t U t i l i t i e s 2 ,


предназначенного для вывода на печать в JDK 1.3. Данный класс является подклассом
P r i n t U t i l i t i e s (см. листинг 15.19) и переопределяет метод p r i n t так, что вместо
15.5. Вывод на печать Swing-компонентов 657

метода paint он вызывает метод print этого компонента. Вывод на печать с помо­
щью данного класса осуществляется посредством следующего выражения:
PrintUtilities2 .printCoinponent {componentToBePrinted) ;
Параметр componentToBePrinted передает методу Swing-компонент, предназна­
ченный для вывода на печать.

Листинг 15.22.PrintUtilities2.java

import java.awt.*;
import javax.swing.*;
import Java.awt.print.* ;
/** Вспомогательный класс, предназначенный для вывода
^ на печать произвольного компонента в JDK 1.3.
* Данный класс использует особенность JDK 1.3,
* состоящую в том, что в классе JComponent метод
* print переопределяется; в нем автоматически
* устанавливается флаг, который запрещает двойную
* буферизацию. Если флаг установлен, paint вызывает
* printComponent, printBorder и printChildren.
•*•
* Для того чтобы вывести компонент, его надо передать
* PrintUtilities2.printComponent(componentToBePrinted)
V
public class PrintUtilities2 extends PrintUtilities {

public static void printComponent (Comiponent c) {


new PrintUtilities2(c) .print 0 ;

public PrintUtilities2(Component componentToBePrinted) {


super(componentToBePrinted);
}
// Универсальная процедура вывода на принтер в JDK 1.3.
// Для вывода на печать в JDK 1.2 надо использовать
// PrintUtilitiesl.
public int print(Graphics g, PageFormat pageFormat,
int pagelndex) {
if (pagelndex > 0) {
return(NO_SUCH_PAGE);
} else {
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pageFormat.getlmageableX(),
pageFormat.getlmageableY());
componentToBePrinted.print(g2d);
return(PAGE_EXISTS);
}
}
658 Глава 15. Расширенные средства Swing

15.6. Потоки Swing


Swing соответствует знакомой вам модели событий AWT и использует для обнов­
ления компонентов один поток. Поскольку рисование компонентов и обработка со­
бытий осуществляются в рамках одного потока передачи событий, при разработке
программ с графическим пользовательским интерфейсом необходимо соблюдать сле­
дующие два правила.
1. Если обработка событий предполагает действия, требующие большого количе­
ства ресурсов центрального процессора, они должны выполняться в отдельном
потоке. Освободив поток передачи событий для обработки остальных элемен­
тов очереди, вы повысите качество пользовательского интерфейса.
2. Изменение состояния отображаемых Swing-компонентов должно осуществ­
ляться в пределах потока доставки событий.
Если вы нарушите данные правила, это может привести к ситуациям, при которых
интерфейсные элементы не будут реагировать на дер1ствия пользователя, либо к воз­
никновению "гонок" при определении состояния компонента.
Для того чтобы проиллюстрировать проблемы, которые могут возникнуть при об­
работке событий, рассмотрим простое приложение, предназначенное для передачи
файла с клиентской машины на удаленный сервер. Предположим, что при создании ин­
терфейса были предусмотрены кнопка Start, при активизации которой начинается пе­
редача файла, и компонент J L a b e l , отображающий ход процесса передачи (информа­
ция о состоянии представляется с помощью сообщений, например " T r a n s f e r r i n g
f i l e n a m e . . . " ) . В простейшем случае метод a c t i o n P e r f o r m e d , вызываемый после
щелчка на кнопке Start, может содержать следующие команды:
/ / Некорректно построенный обработчик событий,
/ / связанных с активизацией кнопки S t a r t ,
p u b l i c void actionPerformed(ActionEvent e v e n t ) {

/ / При изменении объекта JLabel проблемы не возникают,


/ / поскольку действия выполняются в потоке доставки событий.
label.setText("Transferring " + filename);

/ / Передача файла на сервер - для выполнения


/ / операции требуется много времени.
transferFile(filename);

/ / Модификация JLabel происходит корректно, поскольку


/ / осуществляется в потоке доставки событий,
label.setText("Transfer completed.");
}

Здесь изменение текста, отображаемого с помощью объекта JLabel, вполне допус­


тимо, поскольку метод s e t T e x t вызывается в рамках потока передачи событий. Однако
результаты выполнения кода оказываются совершенно неожиданными. При каждом
обращении к методу s e t T e x t в очередь событий помещается PropertyChangeEvent.
Это событие будет извлечено из очереди только после того, как передача файла закон­
чится и метод a c t i o n P e r f o r m e d заверитт свою работу. Таким образом, первое обнов­
ление объекта J L a b e l произойдет лишь после того, как файл будет передан. Второй раз
15.6. Потоки Swing 659

внешний вид компонента JLabel изменится сразу же после первого, поэтому пользова­
тель не увидит сообщения "Transferring . . .". Поскольку передача файла осуществ­
ляется в потоке передачи событий, взаимодействие программы с пользователем будет
блокировано до завершения метода actionPerformed. Такое поведение программы
нельзя назвать приемлемым.
Решением данной проблемы может показаться передача файла и последующее об­
новление компонента JLabel в отдельном потоке. При этом поток доставки событий
освобождается и может быть использован для обработки остальных событий, в част­
ности события, связанного с обновлением JLabel. Рассмотрим модифицированный
методactionPerformed.
// Модифицированный обработчик событий. Действия, требующие
// больших затрат времени, выполняются в отдельном потоке,
public void actionPerformed(ActionEvent event) {
// При изменении объекта JLabel проблемы не возникают,
// поскольку действия выполняются в потоке доставки событий.
ladDel.setText("Transferring " + filename);

// Передача файла требует больших затрат времени,


// поэтому выполняется в отдельном потоке. Поток доставки
// событий освобождается для обработки очереди.
Thread t = new FileTransfer(filename, label);
t. start 0 ;
}
Для поддержки потока используется класс FileTransfer.
// Поток реализован некорректно — в нем выполняется
// обновление компонента Swing.
public class FileTransfer extends Thread {
private String filename;
private JLabel = label;

public FileTransfer(String filename, JLabel label) {


this.filename = filename;
this.label = label;
}
public void run() {
// Передача файла на сервер.
// Эта операция требует много времени.
doTransfег(...);

// Обновление JLabel происходит некорректно. Оно


// должно осуществляться в потоке доставки событий.
IcUDel. setText ( "Transfer complete. ") ;
}
}
Данный подход действительно приводит к освобождению потока доставки собы­
тий. Однако, поскольку изменение состояния объекта J L a b e l происходит в потоке
F i l e T r a n s f e r , второе правило, согласно которому обновление Swing-компонентов
может осуществляться только в потоке доставки событий, нарушается. В этом случае
могут возникнуть "гонки" между обновлением компонента J L a b e l в потоке доставки
660 Глава 15. Расширенные средства Swing

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


В зависимости от распределения ресурсов центрального процессора выполнение
двух потоков можно упрощенно рассматривать как выполнение двух различных эк­
земпляров программы.
Для разрешения подобных проблем Swing предоставляет два метода: i n v o k e L a t e r и
i n v o k e A n d W a i t , которые размещают в очереди событий объекты R u n n a b l e , предна­
значенные для обновления Swing-компонентов. После того как объект R u n n a b l e извле­
кается из очереди, метод r u n объекта вызывается в потоке доставки событий. В тело
этого метода помещается код, выполняющий обновление компонента J L a b e l . Подроб­
но о многопотоковой обработке и объектах R u n n a b l e рассказывается в главе 16.
Методика профессионалов

Чтобы организовать доступ к компонентам Swing из потоков, отлич­


ных от потока обработки событий, необходимо поместить код в объ­
ект Runnable И использовать для включения объекта Runnable в оче­
редь событий один из методов SwlngUtilitles: InvokeLater или
InvokeAndWal t.

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


Класс S w i n g U t i l i t i e s предоставляет два метода, позволяющих поместить объ­
ект R u n n a b l e в очередь событий. Код, предназначенный для внесения изменений в
компонент Swing из потока, определяемого пользователем, должен быть включен в
тело метода r u n объекта R u n n a b l e .

public static void invokeLater (Runnable object)


Метод i n v o k e L a t e r помещает объект R u n n a b l e в очередь событий и немедленно
возвращает управление вызывающему методу. После того как объект достигнет
начала очереди событий и будет извлечен из нее, метод r u n выполняется в потоке
доставки событий. Метод i n v o k e L a t e r можно вызвать либо из потока доставки
событий, либо из любого потока, определяемого пользователем.

public static void invokeAndWait(Runnable object)


throws I n t e r r u p t e d E x c e p t i o n , I n v o c a t i o n T a r g e t E x c e p t i o n
Метод i n v o k e A n d W a i t помещает объект R u n n a b l e в очередь событий и приоста­
навливает свое выполнение до завершения метода r u n объекта R u n n a b l e . Данный
метод используется, если обновление Swing-компонента должно произойти перед
тем, как метод, выполняемый в рамках потока, перейдет к следующей операции,
либо если для продолжения работы необходимо получить данные от конкретного
компонента. Метод i n v o k e A n d W a i t нельзя вызывать из потока доставки событий,
так как это приводит к возникновению тупиковой ситуации. Для того чтобы опре­
делить, является ли текущий поток потоком доставки событий, можно использо­
вать метод i s E v e n t D i s p a t c h T h r e a d . В процессе работы метода i n v o k e A n d W a i t
может быть сгенерировано исключение I n v o c a t i o n T a r g e t E x c e p t i o n , принад­
лежащее пакету J a v a . l a n g . r e f l e c t .
15.6. Потоки Swing 661

Рассмотрим процедуру обновления Swing-компонентов с использованием объекта


Runnable. Вернемся к обсуждению примера, в котором на удаленный сервер переда­
ется файл, а объект JLabel отображает состояние процесса передачи. Как вы уже
знаете, передача файла в потоке доставки событий не позволяет программе взаимо­
действовать с пользователем, а выполнение передачи файла в потоке, определенном
пользователем, противоречит правилу, которое требует, чтобы обновление Swing-
компонентов происходило в рамках потока доставки событий. Нарушение этого пра­
вила может привести к возникновению "гонок". Ниже представлены окончательный
вариант метода a c t i o n P e r f o r m e d и класс F i l e T r a n s f e r . В данном примере обнов­
ление компонента JLabel не противоречит правилам.
/ / Обработчик событий, реализованный корректно. Операции,
/ / требующие больших затрат времени, осуществляются в отдельном
/ / потоке, а модификация компонентов Swing осуществляется
/ / с помощью объекта Runnable, который помещается
/ / в очередь событий.
p u b l i c void actionPerformed(ActionEvent event) {
/ / Задача, для выполнения которой требуется много времени,
/ / переносится в отдельный поток.
Thread t = new FileTransfer(filename, l a b e l ) ;
t.start 0 ;
}
}

П и вызове конструктора FileTransfer ему передается ссылка на объект JLabel,


так что объект может обновлять данный компонент. В теле метода run создаются два
объекта Runnable: один из них предназначен для обновления JLabel перед переда­
чей файла, а другой — для обновления JLabel после завершения передачи файла. Ка­
ждый из объектов Runnable определяется как анонимный класс. О б использовании
анонимных классов см. в главе 10.
// Окончательная версия FileTransfer. Модификация
// компонента осуществляется в соответствии с правилами,
public class FileTransfer extends Thread {
private String filenames-
private JLabel label;

public FileTransfer(String filename, JLabel label) {


this.filename = filename;
this.label = label;
}
public void run() {
try {
// Объект Runnable, предназначенный для обновления
// JLabel, помещается в очередь событий.
// Метод invokeAndWait не возвращает управление до тех
// пор, пока обновление компонента не закончится.
SwingUtilities.invokeAndWait(
new Runnable() {
public void run() {
label.setText("Transferring " + filename);
}
});
662 Глава 15. Расширенные средства Swing

} catch(InvocationTargetException ite) {
} catch(InterruptedException ie) { }

// Передача файла на сервер (длительная процедура).


doTransfer(...);

// Окончательное обновление компонента из объекта


// Runnable. При этом используется метод invokeLater.
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
IcUDel. setText ("Transfer completed") ;
}
});
}
}
Для помещения первого объекта R u n n a b l e в очередь событий используется метод
i n v o k e An d W a i t . Это гарантирует, что перед началом передачи файла компонент
J L a b e l отобразит надпись " T r a n s f e r r i n g . . .". После завершения передачи файла
создается еще один объект R u n n a b l e . Он помещается в очередь событий посредст­
вом метода i n v o k e L a t e r . В этом случае совершенно не обязательно, чтобы требуе­
мая надпись появилась в окне до того, как программа продолжит работу. Когда собы­
тие достигнет начала очереди и будет вызван объект r u n , в компоненте J L a b e l ото­
бразится текст '*Transf е г c o m p l e t e d " .
Как было сказано выше, при разработке программы необходимо следить, чтобы
обновление Swing-компонентов не происходило за пределами потока передачи собы­
тий. Требование организовывать выполнение в отдельном потоке в основном отно­
сится к таким задачам, как организация запросов к базам данных, взаимодействие с
удаленными объектами средствами RMI, обращение к Web-серверам и передача фай­
лов. Если вам необходимо обновлять компоненты из потока, определенного пользо­
вателем, делать это следует с помощью методов i n v o k e L a t e r или i n v o k e A n d W a i t .

15.7. Резюме
Теперь вы знаете достаточно для того, чтобы приступить к созданию профессио­
нальных пользовательских интерфейсов на базе Swing-компонентов. В данной главе
мы рассмотрели лишь некоторые средства, предоставляемые расширенными компо­
нентами. Для того чтобы научиться полностью использовать возможности Swing, вам
необходимо затратить время на их изучение, однако время и усилия окупятся при
создании реальных программ.
Компоненты J L i s t , J T r e e и J T a b l e могут применять для доступа и модификации
данных (а также для генерации событий) модели по умолчанию. П р и необходимости
вы можете разработать собственные хмодели данных и средства визуализации, преду­
смотрев в них особенности взаимодействия с данными и их отображения, специфи­
ческие для создаваемой вами программы. В состав списков, деревьев и таблиц можно
включить практически любые Swing-компоненты, но чтобы они корректно отобража­
лись, необходимо реализовать соответствующие средства визуализации.
15.6. Потоки Swing 663

Вывод компонентов на печать требует наличия класса, в котором реализован ме­


тод p r i n t . Этот метод не обязательно должен присутствовать в классе компонента;
для поддержки печати часто создают вспомогательные классы. При модификации
Swing-компонентов необходимо соблюдать правила, определяющие работу с потока­
ми. Методы invokeAndWait и i n v o k e L a t e r позволяют перенести действия по мо­
дификации компонента в поток передачи событий.
ИСПОЛЬЗОВАНИЕ
ПОТОКОВ

В ЭТОЙ главе...

Организация потоков с помощью объектов Thread.

Организация потоков с помощью существующих объектов

Проблемы, связанные с организацией потоков,


и их решение.

Синхронизация доступа к разделяемым ресурсам.

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

Вывод графических изображений в многопотоковом


режиме.

Использование двойной буферизации.

Анимационные изображения.

Использование таймеров.
J~y\ZJ^ZJ

ava предоставляет разработчику один из самых мощных среди существующих в на­

J стоящее время инструментов для одновременного выполнения различных частей


программы — класс T h r e a d . Потоки иногда называют "легковесными процессами";
эти процессы совместно используют область памяти, "кучу" (heap), но каждый из
них поддерживает свой стек. Программы, разработанные с использованием потоков,
имеют следующие преимущества.

Эффективность
Некоторые задачи быстрее выполняются программами, использующими потоки.
Рассмотрим, например, копирование пяти текстовых файлов с удаленного узла.
Предположим, что для установления сетевого соединения требуется 12 секунд, а
после того, как соединение установлено, файл может быть скопирован за 1 секун­
ду. Если копировать пять файлов один за другим, вся процедура займет 5 х 13 = 65
секунд. Если же все файлы будут копироваться в различных потоках, то копирова­
ние займет 12 + 5 = 17 секунд. Выигрыш в производительности при выполнении
подобных задач настолько велик, что Java автоматически копирует изображения в
отдельных потоках (подробно о копировании изображений см. в главе 9).

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

Н о в ы е возмолсности
Некоторые задачи попросту не могут быть реализованы без поддержки потоков.
Например, HTTP-сервер должен ожидать установления сетевого соединения. При
обращении к серверу соединение передается новому процессу, который выполня­
ет обработку запроса. Если бы потоки не поддерживались, то на все время обслу-
666 Глава 16. Использование потоков

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

1 6 . 1 . Запуск потоков
Java предоставляет два механизма организации многопотоковых программ: созда­
ние подкласса класса T h r e a d , содержащего код, предназначенный для выполнения, и
использование экземпляра класса T h r e a d для вызова кода исходного объекта.

Выполнение действий в отдельном объекте Thread


Для того чтобы организовать поток первым из указанных двух способов, надо соз­
дать подкласс класса T h r e a d , включить необходимые команды в метод r u n подкласса,
создать экземпляр подкласса и вызвать метод s t a r t этого экземпляра. Данный под­
ход демонстрируется в листингах 16.1 и 16.2.

Листинг 1 6 . 1 . D r i v e r C l a s s . J a v a
public class DriverClass extends SomeClass
public void startAThread() {
/ / Создание о б ъ е к т а T h r e a d .
T h r e a d C l a s s t h r e a d = new T h r e a d C l a s s ( ) ;
/ / Запуск отдельного процесса,
thread.start();

}
}

Листинг 16.2.ThreadClass.Java

public class ThreadClass extends Thread {


public void run() {
// Реализация поведения потока.
}
}
1 6 . 1 . З а п у с к потоков 667

Если класс является подклассом Thread, он наследует метод s t a r t , который вы­


зывает метод run и обеспечивает его выполнение в отдельном потоке. После завер­
шения метода run процесс перестает существовать. Несмотря на то что ваш код со­
держится в теле метода run, для запуска потока необходимо вызвать метод s t a r t . Ес­
ли вы обратитесь непосредственно к run, код будет выполнен в том же потоке, что и
вызывающий метод. Данные, локальные для потока, обычно помещаются в локаль­
ные переменные метода run либо в переменные экземпляра, объявленные как
p r i v a t e . Внешние данные становятся доступными потоку, если ссылка на них была
передана конструктору потока. Кроме того, из метода run можно обращаться к мето­
дам и переменным, объявленным как p u b l i c .
Внимание!

Не вызывайте метод run непосредственно. Если вы поступите таким


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

Примером использования потоков может служить класс C o u n t e r , код которого


представлен в листинге 16.3. Объект этого класса выводит числа от О до N, делая меж­
ду операциями отображения данных паузы произвольной длительности. Класс, вы­
полняющий тестирование (листинг 16.4), создает и запускает несколько экземпляров
C o u n t e r . Результаты выполнения программы показаны в листинге 16.5.

Листинг 16.3. Counter.java

/** Подкласс класса Thread, который выводит числа от О до N,


* делая между ними паузы произвольной длительности.
V
p u b l i c c l a s s Counter e x t e n d s Thread {
p r i v a t e s t a t i c i n t totalNum = 0;
p r i v a t e i n t currentNum, l o o p L i m i t ;
p u b l i c C o u n t e r ( i n t loopLimit) {
t h i s . l o o p L i m i t = loopLimit;
currentNum = totalNum++;
}
p r i v a t e void p a u s e ( d o u b l e seconds) {
t r y { Thread, s l e e p (Math, round (100.0. O*seconds) ) ; }
c a t c h ( I n t e r r u p t e d E x c e p t i o n ie) {}
}
/** После завершения работы run поток прекращает
* свое существование.

public void run() {


for(int i=0; i<loopLimit; i++) {
System.out.println("Counter " + currentNum + ": " + i) ;
pause(Math.random()); // Перевод в "спящий" режим на
// время до 1 секунды.
}
}
668 Глава 16. Использование потоков

Листинг 16.4.CounterТеSt.Java

/** Запуск нескольких экземпляров класса Counter. */

public class CounterTest {


public static void main(String[] args) {
Counter cl = new Counter(5);
Counter c2 = new Counter (5);
Counter c3 = new Counter(5);
cl.start 0 ;
c2.start();
c3.start 0 ;

Листинг 16.5. Выходные данные CounterTest

Counter 0: 0
Counter 1: 0
Counter 2: 0
Counter 1: 1
Counter 2: 1
Counter 1: 2
Counter 0: 1
Counter 0: 2
Counter 1: 3
Counter 2: 2
Counter 0: 3
Counter 1: 4
Counter 0: 4
Counter 2: 3
Counter 2: 4

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


интерфейс Runnable
Существует другой способ организации многопотоковых вычислений. П р и ис­
пользовании этого способа создается класс, реализующий интерфейс R u n n a b l e , а за­
тем — экземпляр класса T h r e a d , причем конструктору T h r e a d передается ссылка на
экземпляр класса R u n n a b l e , а потом вызывается метод s t a r t класса T h r e a d . Необ­
ходимые действия реализуются в теле метода r u n класса R u n n a b l e ; метод r u n класса
T h r e a d не используется. Из метода r u n можно обращаться к любым переменным и
методам основного класса ( R u n n a b l e ) . Который из методов должен выполняться
( r u n класса R u n n a b l e или r u n класса T h r e a d ) , определяется тем, какой объект был
передан конструктору. Если при создании экземпляра класса T h r e a d конструктору
класса был передан объект R u n n a b l e , метод s t a r t вызовет метод r u n этого класса.
Данный подход демонстрируется в листинге 16.6.
1 6 . 1 . Запуск потоков 669

Листинг 16.6.ThreadedClass.Java

public c l a s s ThreadedClass extends AnyClass implements Runnable {


public void run() {
/ / Реализация поведения потока.
}

p u b l i c void s t a r t T h r e a d ( ) {
Thread t = new Thread(this);
t . s t a r t O ; / / Вызов метода run объекта, на который
/ / указывает параметр, передаваемый конструктору.

Метод run класса, реализующего интерфейс Runnable, можно запустить в не­


скольких потоках. При каждом вызове run создаются отдельные копии локальных
переменных, однако переменные класса совместно используются всеми потоками.
Вопросы одновременного доступа к данным будут рассматриваться в следующем раз­
деле. Для того чтобы обратиться в экземпляру потока и получить доступ к данным,
используемым в этом потоке, надо использовать метод Thread. c u r r e n t T h r e a d ().
В листинге 16.7 приведен пример применения описанного подхода к организации
многопотокового выполнения программ. Заметьте, что класс, код которого представ­
лен в листинге 16.8, не вызывает метод s t a r t объекта Counter2, поскольку класс
Counter2 не является подклассом Thread и не содержит метод s t a r t . Результаты
выполнения программы показаны в листинге 16.9 и выглядят так же, как и результа­
ты, полученные с помощью класса Counter.

Листинг 16.7. Counter2.java

/** Класс Runnable, который выводит числа от О до N,


* делая между ними паузы произвольной длительности.
Ч
public c l a s s Counter2 implements Runnable {
p r i v a t e s t a t i c i n t totalNum = 0;
p r i v a t e i n t currentNum, loopLimit;
p u b l i c C o u n t e r 2 ( i n t loopLimit) {
t h i s . l o o p L i m i t = loopLimit;
currentNum = totalNum++;
Thread t = new Thread(this);
t.startO ;

p r i v a t e void pause(double seconds) {


t r y { Thread.sleep(Math.round(1000.O*seconds));
c a t c h ( I n t e r r u p t e d E x c e p t i o n ie) {}

p u b l i c void run() {
670 Глава 16. Использование потоков

for(int i=0; i<loopLimit; i++) {


System.out.println("Counter " + currentNum + ": " + i ) ;
pause(Math.random()); // Перевод в "спящий" режим на
// время до 1 секунды.
}
}
}

Листинг 16.8. Covinter2Test. Java

/** Запуск нескольких экземпляров класса Counter2.

public class Counter2Test {


public static void main(String[] args) {
Counter2 cl = new Counter2(5)
Counter2 c2 = new Counter2(5)
Counter2 c3 = new Counter2(5)
}
}

Листинг 16.9. Результаты выполнения Counter2Test

Counter 0: 0
Counter 1: 0
Counter 2: 0
Counter 1: 1
Counter 1: 2
Counter 0: 1
Counter 1: 3
Counter 2: 1
Counter 0: 2
Counter 0: 3
Counter 1: 4
Counter 2: 2
Counter 2: 3
Counter 0: 4
Counter 2: 4

Глядя на данный пример, может показаться, что описываемый здесь способ слож­
нее, чем создание подкласса Thread. Однако, как вы знаете, Java не допускает множе­
ственное наследование, поэтому если ваш класс уже является подклассом какого-то
класса (например, Applet), то он не может быть подклассом класса Thread. Кроме
того, в данном случае метод run имеет полный доступ к переменным и методам клас­
са. При использовании подкласса Thread для обеспечения доступа к основному классу
приходится принимать специальные меры. Организуя многопотоковую обработку в
аплете, создание и запуск потоков лучше всего выполнять в методе s t a r t аплета.
16.2. Возникновение "гонок" 671

16.2. Возникновение "гонок"


Код класса, реализующего интерфейс Runnable, может выполняться одновременно
в нескольких потоках. Однако при этом считается, что команды, выполняющиеся в раз­
ных потоках, принадлежат одному экземпляру класса. Данные, отдельно обрабатывае­
мые в каждом потоке, содержатся в локальных переменных метода run. Организация
доступа к переменным экземпляра, а также к данным, содержащимся в других классах,
требует особого внимания, поскольку к одним и тем же переменным может происхо­
дить обращение из нескольких потоков. В листинге 16.10 содержится некорректный
код аплета. Прежде чем продолжить изучение материала, просмотрите код метода run
и постарайтесь понять, какие проблемы могут возникн)'ть при его выполнении.

Листинг 16.10. BuggyCounterApplet. Java

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
/** Аплет запускает несколько экземпляров своего метода run
* в разных потоках. Данный аплет работает корректно
* в большинстве случаев, но не всегда!
V
public c l a s s BuggyCounterApplet extends Applet
implements Runnable{
p r i v a t e i n t totalNum = 0;
p r i v a t e i n t loopLimit = 5;
/ / Следует различать метод s t a r t класса Thread и метод s t a r t
/ / класса Applet, который вызывается после завершения
/ / метода i n i t .
p u b l i c void s t a r t О {
Thread t ;
for(int i=0; i<3; i++) {
t = new Thread(this);
t. start 0 ;
}
}
private void pause(double seconds) {
try { Thread.sleep(Math.round(1000.O*seconds)); }
catch(InterruptedException ie) {}
}
public void run() {
int currentNum = totalNxom;
System.out.printlnCSetting currentNum to " + currentNum);
totalNum = totalNum + 1;
for(int i=0; i<loopLimit; i++) {
System, out .printlnC Counter " + currentNijm + ": " + i) ;
pause(Math.random());
}
}
672 Глава 16. Использование потоков

Листинг 16.11. Выходные данные BuggyCounterApplet

> appletviewer BuggyCounterApplet.html


Setting currentNuin to 0
Counter 0: 0
Setting currentNum to 1
Counter 1: 0
Setting currentNum to 2
Counter 2 0
Counter 2 1
Counter 1 1
Counter 0 1
Counter 2 2
Counter 0 2
Counter 1 2
Counter 1 3
Counter 0 3
Counter 2 3
Counter 1 4
Counter 2 4
Counter 0 4

В большинстве случаев аплет выполняется корректно (выходные данные показа­


ны в листинге 16.11), и у разработчика может создаться впечатление, что он не допус­
тил ошибки при создании программы. Однако результаты будут правильными только
в том случае, когда между тем, как метод r u n прочитает и инкрементирует значение
t o t a l N u m , ни один из потоков не изменит значение данной переменной. Таким обра­
зом, результат выполнения программы зависит от того, какой из потоков "выиграет
гонку" при инкрементировании t o t a l N u m . П р и многократных запусках аплета рано
или поздно возникнет ситуация, подобная представленной в листинге 16.12.

Листинг 16.12. Выходные данные BuggyCounterApplet, полученные при


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

> appletviewer BuggyCounterApplet.html


Setting currentNum to 0
Counter 0: 0
Setting currentNum to 1
Setting currentNum to 1
Counter 0: 1
Counter 1: 0
Counter 1: 0
Counter 0: 2
Counter 0: 3
Counter 1: 1
Counter 0: 4
Counter 1: 1
Counter 1: 2
Counter 1: 3
Counter 1: 2
Counter 1; 3
Counter 1: 4
Counter 1: 4
16.3. Синхронизация 673

"Решением" данной проблемы может показаться создание кода, в котором чтение


и модификация t o t a l N u m выполнялись бы в рамках одного выражения.
public void run() {
i n t currentNum = totalNiim++;
S y s t e m . o u t . p r i n t l n ( " S e t t i n g currentNum t o " +
currentNum);
f o r ( i n t i = 0 ; i < l o o p L i m i t ; i++> {
S y s t e m . o u t . p r i n t l n ( " C o u n t e r " + currentNum +
": " + i) ;
p a u s e (Math. random () ) ;
}
}

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

16.3. Синхронизация
Механизм синхронизации позволяет управлять доступом к разделяемым ресурсам.
При синхронизации фрагмента кода с этим фрагментом связывается объект блоки­
ровки. Когда поток начинает выполнение синхронизированного фрагмента кода,
этот фрагмент блокируется. До окончания выполнения синхронизированного фраг­
мента либо то тех пор, пока блокировка не будет явно отменена, ни один другой по­
ток не может начать выполнение аналогичного фрагмента кода. Синхронизация мо­
жет распространяться более чем на один фрагмент. Можно использовать в качестве
дескриптора синхронизации объект O b j e c t и задать условия блокировки так, что при
выполнении синхронизированного фрагмента будут блокированы все остальные
фрагменты, помеченные тем же дескриптором синхронизации.

Синхронизация фрагмента кода


Для того чтобы синхронизировать фрагмент кода, осуществляющий доступ к раз­
деляемым ресурсам, надо включить этот фрагмент в блок s y n c h r o n i z e d .
synchronized(someObject) {
// код
}
674 Глава 1 6 . Использование потоков

Выражение s y n c h r o n i z e d сообщает системе о том, что потоки, содержащие дан­


ный фрагмент, должны быть блокированы до тех пор, пока выполнение фрагмента
не будет окончено, либо до тех пор, пока блокировка не будет снята в потоке с помо­
щью команды w a i t . Сказанное не означает, что выполнение синхронизированного
фрагмента не может быть прервано. Планировщик потоков может прекратить работу
синхронизированного кода и передать управление другому потоку. Однако при этом в
потоке, получившем управление, может выполняться только код, отличный от син­
хронизированного.
Заметьте, что подобным способом блокируются фрагменты кода, а не объекты. Каж­
дый объект содержит флаг, который может быть использован для контроля блоки­
ровки. Использование s o m e O b j e c t в качестве дескриптора не означает, что этот
объект блокируется; выражение s y n c h r o n i z e d лишь устанавливает соответствую­
щий флаг. Другие потоки могут по-прежнему получать управление и даже могут воз­
никать "гонки" при попытке доступа к тому же ресурсу, к которому происходит обра­
щение из синхронизированного блока. Однако подобное поведение продемонстри­
руют лишь части программы, не защищенные с помощью выражения s y n c h r o n i z e d .
Одним дескриптором могут помечаться различные блоки кода. П р и этом, если в од­
ном из потоков начнется выполнение фрагмента, помеченного объектом
s o m e O b j e c t , ни один из фрагментов, для которых установлен тот же дескриптор
s o m e O b j e c t , не получит управление. Такая ситуация будет продолжаться до тех пор,
пока фрагмент не закончит выполнение либо пока блокировка не будет снята.

На з а м е т к у

Выражение synchronized6noKHpyeT фрагмент кода, а не объект.


#
Синхронизация метода
Если вы хотите синхронизировать весь метод, сделать это можно с помощью клю­
чевого слова s y n c h r o n i z e d .
p u b l i c s y n c h r o n i z e d v o i d someMethodO {
/ / Тело м е т о д а .
}

Приведенное выражение синхронизирует someMethod, причем в качестве деск­


риптора синхронизации используется текущий экземпляр объекта (т.е. t h i s ) . Как
только в одном из потоков начинается выполнение метода someMethod, ни один дру­
гой поток не имеет права вызывать данный метод или выполнять любой блок кода,
помеченный текущим объектом ( t h i s ) . Таким образом, два приведенных ниже при­
мера эквивалентны.
p u b l i c s y n c h r o n i z e d v o i d someMethodO {
/ / Тело м е т о д а .
}

p u b l i c v o i d someMethodO {
synchronized(this) {
/ / Тело м е т о д а .
}
}
1 6 . 3 . Синхронизация 675

Прочитав о катастрофических последствиях возникновения "гонок", начинающие


программисты пытаются синхронизировать все, что возможно. Однако такой подход
существенно снижает быстродействие программы и приводит к тому, что большие
фрагменты кода в различных потоках выполняются без прерываний. Крайним про­
явлением подобного стремления была бы синхронизация самого метода r u n . Н о при
этом различные потоки выполнялись бы последовательно один за другим и многопо­
токовая организация программ потеряла бы смысл.
Статический метод, объявленный как s y n c h r o n i z e d , ведет себя так же, как и ме­
тод экземпляра, тело которого синхронизировано с помощью объекта класса. Моди­
фикатор s y n c h r o n i z e d не наследуется, поэтому, чтобы метод, переопределенный в
подклассе, был синхронизирован, надо явно задать для него ключевое слово
synchronized.
Внимание!

Метод, переопределенный в подклассе, не наследует ключевое


слово synchronized.

Ошибки, допускаемые при использовании


синхронизации
Создавая подклассы класса T h r e a d , разработчики часто совершают одну ошибку. Они
используют в качестве дескриптора синхронизации объект t h i s , в то время как данные
совместно используются различным экземплярами класса. Рассмотрим следующий при­
мер, где класс S o m e T h r e a d e d C l a s s содержит объект s o m e S h a r e d O b j e c t , объявленный
как s t a t i c . Этот объект доступен всем методам класса и используется различными его эк­
земплярами. Для того чтобы предотвратить возникновение 'YOHOK" между различными
потоками и обеспечить целостность данных, содержащихся в разделяемом объекте, метод
d o S o m e O p e r a t i o n объявлен как s y n c h r o n i z e d . Будут ли другие потоки блокироваться
каждый раз, когда метод d o S o m e O p e r a t i o n выполняет действия с данными?
p u b l i c c l a s s SomeThreadedClass e x t e n d s Thread {
p r i v a t e s t a t i c RandomClass s o m e S h a r e d O b j e c t ;

p u b l i c synchronized void doSomeOperation() {


accessSomeSharedObject();
}

public void run() {


while(someCondition) {
doSomeOperation0; / / Доступ к разделяемым данным.
d o S o m e O t h e r O p e r a t i o n ( ) ; / / Работа с обычными данными.
}
}
}
Как вы знаете, объявление метода как s y n c h r o n i z e d эквивалентно выражению
synchronized(this){
676 Глава 16. Использование потоков

Однако для различных экземпляров S o m e T h r e a d e d C l a s s переменные t h i s ссы­


лаются на разные объекты. Поэтому при выполнении одного из методов do Some-
O p e r a t i o n методы в других потоках не блокируются. Чтобы исправить подобную
ошибку, надо выбрать вместо t h i s другой дескриптор синхронизации.

Синхронизация с помощью разделяемых данных


Одним из решений рассмотренной выше проблемы является использование в ка­
честве дескриптора синхронизации объекта, содержащего разделяемые данные. Дан­
ные вн}тгри объекта по-прежнему не блокируются. Объект лишь сообщает другим по­
токам, что фрагменты кода с тем же дескриптором не могут выполняться до тех пор,
пока текущий фрагмент не завершит свою работу.
p u b l i c void doSomeOperation() {
synchronized(someSharedObject) {
accessSomeSharedObject();
}
}

Использование для синхронизации объекта Class


Еще одно решение состоит в использовании объекта C l a s s . Для представления
информации о классе Java использует уникальный объект C l a s s . Приведенный ниже
код демонстрирует данный подход.
public void doSomeOperation() {
synchronized(SomeThreadedClass.class) {
accessSomeSharedObject();
}
}
Заметьте, что при синхронизации статического метода в роли дескриптора высту­
пает не t h i s , а объект C l a s s .

Синхронизация с помощью произвольного объекта


Для использования в качестве дескриптора синхронизации можно создать новый
произвольный объект, поместив ссылку на него в переменную, объявленную как
s t a t i c . Подобный подход использован в следующем примере:
p u b l i c c l a s s SomeThreadedClass e x t e n d s Thread {
p r i v a t e s t a t i c O b j e c t l o c k O b j e c t = new O b j e c t ( ) ;

p u b l i c void doSomeOperation() {
synchronized(lockObject) {
accessSomeSharedObject();

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

Проблемы, связанные с тем, что переменная t h i s указывает на различные экзем­


пляры класса, не возникают, если для организации потоков используется класс
R u n n a b l e . В этом случае в разных потоках выполняется один и тот же метод r u n . По­
этому в качестве дескриптора синхронизации допускается использование перемен­
ной t h i s . Это можно сделать как явно, формируя синхронизированный блок, так и
неявно, указывая при объявлении метода модификатор s y n c h r o n i z e d .

16.4. Создание многопотоковых методов


Написав программу, вы, возможно, однажды увидите, что созданный вами код на­
до преобразовать в многопотоковый. Как правило, такая необходимость возникает
при переносе программы из однопользовательской среды в многопользовательскую.
Очевидно, что последовательное обслуживание нескольких пользователей неприем­
лемо. Возможно, что в вашем приложении выполняются расчеты, которые занимают
много времени, и выполнение программы желательно организовать так, чтобы поль­
зователь не ожидал, когда расчеты будут окончены.
Во многих случаях можно создать новый класс, выполняющий требуемую задачу в
фоновом режиме, не внося изменений в исходный класс. Для этого надо поступить
следующим образом: создать подкласс вашего класса, объявить этот подкласс как
R u n n a b l e и переопределить нужный метод так, чтобы в нем организовывались но­
вые потоки, а затем вызывался соответствующий метод суперкласса. Применить дан­
ный подход можно в том случае, если выполняются следующие два правила.

• Метод выполняет только поставленную перед ним задачу: побочные эффекты,


выражающиеся в изменении значений переменных, отс)'тствуют.
• Метод не возвращает никаких данных; при определении метода для него ука­
зывается тип v o i d .
Если не будет выполняться первое требование и метод будет модифицировать пе­
ременные (независимо от того, принадлежат они классу или находятся за его преде­
лами), могут возникнуть "гонки". Второе требование также необходимо, поскольку' в
противном случае вызывающая программа будет ожидать завершения метода. Если
одно из двух событий не выполняется, вам придется вносить изменения в исходный
класс, чтобы предотвратить "гонки". В качестве примеров использования данного
подхода можно привести запись сообщений в файлы протоколов, поддержку клиен­
тов HTTP-сервером и обработку почтовых сообщений SMTP-сервером.
Вышесказанное иллюстрирует фрагмент кода, приведенный ниже. В классе
S o m e C l a s s содержится метод f o o , которому передается один параметр— экземпляр
класса R a n d o m C l a s s r a n d o m A r g . Метод f o o не вызывает побочных эффектов, т. е. не
модифицирует переменные за пределами метода. Данный фрагмент представляет со­
бой заготовку, предназначенную для организации многопотокового выполнения ме­
тода f o o без модификации исходного класса.
p u b l i c c l a s s T h r e a d e d S o m e C l a s s e x t e n d s SomeClass
implements Runnable {
/ / Метод foo объявлен к а к v o i d , т . е . не возвращает
/ / никаких з н а ч е н и й ,
p u b l i c v o i d f o o ( R a n d o m C l a s s randomArg) {
678 Глава 16. Использование потоков

MyThread t = new M y T h r e a d ( t h i s , randomArg);


t.start 0;
}

p u b l i c void run() {
MyThread t = ( M y T h r e a d ) T h r e a d . c u r r e n t T h r e a d ( ) ;
RandomClass randomArg =
(RandomClass)t.getValueSavedEarlier();
super.foo(randomArg);
}
}
Благодаря тому, что вызов реального метода f o o ( s u p e r . f o o ) перенесен в новый
поток, переопределенный метод f o o немедленно возвращает управление, а реальная
задача продолжает выполняться в фоновом режиме.
Класс MyThread имеет следующий вид:
p u b l i c c l a s s MyThread e x t e n d s T h r e a d {
p r i v a t e Object d a t a ;
p u b l i c MyThread(Runnable r u n n a b l e , Object d a t a ) {
super(runnable);
this.data = data;
}
public Object getValueSavedEarlier() {
return data;
}
}
Параметр метода f o o , randomArg, передается конструктору MyThread и сохраня­
ется в локальной копии внутри MyThread, предотвращая тем самым возникновение
"гонок". Сохранение копии r a n d o m A r g вн)'три f o o не устраняет условий возникнове­
ния "гонок", так как randomArg может быть изменен в интервале времени между соз­
данием потока и вызовом в том же потоке s u p e r , f o o . Синхронизация внутри f o o
также не решает проблему, поскольку выполнение программы продолжается в другом
методе. При выполнении потока данные, сохраненные ранее, извлекаются и выпол­
няется метод f o o суперкласса.
При создании заготовки класса MyThread предполагается, что методу f o o каждый
раз передается "новый" экземпляр класса R a n d o m C l a s s либо R a n d o m C l a s s не допус­
кает изменений внутренних переменных (как, например, классы S t r i n g , I n t e g e r ,
F l o a t , D o u b l e и др.) Поэтому MyThread сохраняет только ссылку на объект
randomArg. Если данное предположение несправедливо, в классе MyThread прихо­
дится клонировать объект.
this.data = data.clone();
Метод c l o n e класса RandomClass должен выполнять "глубокое" клонирование,
т.е. копировать все внутренние объекты (простые типы копируются по умолчанию).
Пример создания фонового процесса для класса, который первоначально не под­
держивал несколько потоков, показан в листингах 16.13 и 16.14. Класс RSAKey содержит
метод c o m p u t e Key, который предназначен для построения пары из общедоступного и
личного ключей, соответствующих стандарту RSA. Минимальное количество цифр об­
щедоступного ключа задается с помощью параметра, передаваемого методу. Построен­
ная пара RSA, а также модуль N выводится с помощью S y s t e m , o u t . Исходный код дан­
ного примера можно найти по адресу h t t p : / / c o r e w e b p r o g r a m m i n g . com/.
16.4. Создание многопотоковых методов 679

Листинг 16.13. RSAKey. Java

import Java.math.Biglnteger;

/** Создание пары ключей RSA, содержащей указанное количество цифр.


Ч
public class RSAKey {
private static final Biglnteger ONE = new Biglnteger("1");

// Определение ключей для кодирования и декодирования.


// Чтобы закодировать целочисленное значение М, надо
// вычислить выражение R = М^е mod N. Для декодирования
// R используется выражение М = R'^d mod N,
// где е — общедоступный ключ, a d — личный.

public void computeKey(String strNumDigits) {


Biglnteger p, q, n, m, encrypt, decrypt;
int numDigits = Integer.parseint(strNumDigits);
if (numDigits%2==l) {
numDigits++;
}
do {
p = Primes.nextPrime(Primes.random(numDigits/2));
q = Primes.nextPrime(Primes.random(numDigits/2));

n = p.multiply(q);
m = (p.subtract(ONE)).multiply(q.subtract(ONE));

// Поиск ключа для кодирования.


encrypt = Primes.nextPrime(Primes.random(numDigits));
while (!encrypt.gcd(m).equals(ONE)) {
encrypt = Primes.nextPrime(encrypt);
}
// Определение ключа для декодирования.
decrypt = encrypt.modlnverse(m);
// Ключи для кодирования и декодирования должны иметь
// одинаковое количество цифр (numDigits).
}while ((decrypt.toString().length() != numDigits) ||
(encrypt.toString().length() != numDigits) ||
(n.toString().length() != numDigits));
System.out.println("\nN => " + n ) ;
System.out.println("public => " + encrypt);
System.out.println("private => " + decrypt);
680 Глава 16. Использование потоков

Листинг 1 6 . 1 4 . ThreadedRSAKey. Java

import java.io.*;
/** Пример выполнения в фоновом режиме метода, который
* первоначально предназначался для работы в однопотоковом
* режиме. В обычных условиях выполнение потока команд
* должно быть приостановлено до завершения computeKey.

public class ThreadedRSAKey extends RSAKey implements Runnable {

// Значение strNumDigits хранится в потоке для


// предотвращения возникновения "гонок".
public void computeKey(String strNumDigits) {
RSAThread t = new RSAThread(this, strNumDigits);
t.start 0 ;
}
// Получение strNumDigits и вызов исходного метода.
// Теперь вычисления выполняются в фоновом режиме,
public void run() {
RSAThread t = (RSAThread)Thread.currentThread();
String StrNumDigits = t.getStrDigits();
super.computeKey(StrNumDigits);

public static void main(String[] args){


ThreadedRSAKey key = new ThreadedRSAKey()
for (int i=0; i<args.length ; i++) {
key.computeKey(args[i]);
}
}
}

class RSAThread extends Thread {


protected String strNumDigits;

public RSAThread(Runnable rsaObject, String strNumDigits) {


super(rsaObject);
this.StrNumDigits = strNumDigits;
}

public String getStrDigits () {


return(StrNumDigits);

Вычисление кодов, содержащих большое количество ц и ф р , требует затраты зна­


чительных ресурсов центрального процессора, поэтому решение организовать вы­
полнение метода c o m p u t e K e y в виде фонового процесса выглядит вполне естествен­
ным. Класс ThreadedRSAKey представляет многопотоковый вариант computeKey;
здесь локальная копия параметра сохраняется в экземпляре RSAThread. П р и м е р вы-
16.5. Методы класса Thread 681

ходных данных, сгенерированных ThreadedRSAKey, показан в листинге 16.15. Как


видно из приведенного листинга, вычисление пары ключей, содержащих меньшее
количество ц и ф р , заканчивается раньше, чем вычисление пары с большим количест­
вом ц и ф р .
До октября 2000 г. RSA-алгоритм был законодательно защищен (United States Pat­
ent No. 4,405,829), теперь его можно использовать для кодирования и декодирования
данных без лицензии.

Л и с т и н г 1 6 . 1 5 . Выходные д а н н ы е ThreadedRSAKey

> j a v a ThreadedRSAKey 50 8

N => 22318033
p u b l i c => 99371593
p r i v a t e => 13439917
N => 80587 805972834 4259805164 31184 4 824160199414 998 4 603 9
p u b l i c => 8214 567 32107 9385034 667 0822324 9107 04 7 4 3113 917 4 81417
p r i v a t e => 54738576754079530157967908359197723401677283881913

Еще один пример создания многопотокового метода в классе, который первона­


чально поддерживал только однопотоковое выполнение, будет приведен в главе 17.
Таким способом мы создадим простой HTTP-сервер, поддерживающий взаимодейст­
вие с клиентами. Как вы увидите, после создания многопотокового варианта метода
h a n d l e C o n n e c t i o n число клиентов, которых может обслужить HTTP-сервер за фик­
сированный интервал времени, ограничивается только скоростью создания объектов
S o c k e t и скоростью создания новых потоков.

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


Ниже описаны конструкторы, константы и методы, определенные в классе
T h r e a d . Здесь же приведены сведения о методах w a i t , n o t i f y и n o t i f y A l l , кото­
рые на самом деле унаследованы T h r e a d от класса Ob j e c t .

Конструкторы
public ThreadO
Использование данного типа конструктора для исходного класса T h r e a d не оп­
равдано, поскольку при создании потока будет вызван метод r u n , не содержащий
ни одной команды. Однако конструктор без параметров часто применяется в под­
классах класса T h r e a d , где переопределен метод r u n . Выражение new T h r e a d ()
эквивалентно выражению new T h r e a d ( n u l l , n u l l , "Thread-N") , где N автома­
тически выбирается системой.

public Thread(Runnable target)


Если данный констр}т<тор используется для создания объекта T h r e a d , то при вызове
метода s t a r t будет автоматически вызываться метод r u n объекта, переданного
682 Глава 16. Использование потоков

конструктору в качестве параметра. Этот конструктор эквивалентен T h r e a d ( n u l l ,


t a r g e t , "Thread-N").

public T h r e a d ( T h r e a d G r o u p g r o u p , R u n n a b l e target)
Данный метод организует поток, в котором выполняется метод r u n объекта
R u n n a b l e . Ссылка на объект передается в составе параметра t a r g e t . Поток поме­
щается в указанную группу T h r e a d G r o u p (если метод c h e c k A c c e s s позволяет сде­
лать это). Объект T h r e a d G r o u p определяет группу потоков и позволяет управлять
этими потоками как единым целым. Подробнее о группировке потоков рассказыва­
ется ниже. Строго говоря, все потоки принадлежат объектам T h r e a d G r o u p . Если
параметр, определяющий группу, не указан, используется группа, к которой принад­
лежит текущий поток. Данный конструктор эквивалентен T h r e a d ( g r o u p , t a r g e t ,
"Thread-N").

public Thread(String name)


Если при создании потока не указано имя, он автоматически получает имя в фор­
мате " T h r e a d - N " . Указанный конструктор позволяет явно задать имя потока. Для
получения имени используется метод g e t Name. Рассматриваемый конструктор эк­
вивалентен T h r e a d ( n u l l , n u l l , n a m e ) . Это значит, что при создании нового
потока в нем будет выполняться метод r u n объекта Thread, а созданный поток по­
мещается в группу, к которой принадлежит вызывающий поток.

public T h r e a d ( T h r e a d G r o u p g r o u p , String n a m e )
Этот конструктор создает в указанной группе поток с заданным именем. П р и за­
пуске потока вызывается метод r u n класса T h r e a d . Данный конструктор эквива­
лентен T h r e a d ( g r o u p , n u l l , n a m e ) .

public T h r e a d ( R u n n a b l e target, String name)


Данный конструктор создает поток с заданным именем, используя указанный объ­
ект R u n n a b l e . П р и запуске потока вызывается метод r u n объекта t a r g e t . Ука­
занный конструктор эквивалентен T h r e a d ( n u l l , t a r g e t , n a m e ) .

public T h r e a d ( T h r e a d G r o u p g r o u p , R u n n a b l e target. String name)


Данный конструктор создает в составе указанной группы поток с заданным име­
нем, использующий указанный объект R u n n a b l e . Если значение параметра g r o u p
не равно n u l l , новый поток помещается в группу либо метод c h e c k A c c e s s гене­
рирует исключение S e c u r i t y E x c e p t i o n . Если вместо параметра g r o u p задано
значение n u l l , новый поток создается в группе вызывающего потока. Если пара­
метр t a r g e t равен n u l l , в потоке вызывается метод r u n объекта T h r e a d .

Константы
public final int MAX_PRIORITY
Данная константа определяет максимальный п р и о р и т е т потока.

public final int MIN__PRIORITY


Данная константа определяет минимальный п р и о р и т е т потока.
16.5. Методы класса Thread 683

p u b l i c final int N O R M _ P R I O R I T Y
Данный приоритет соответствует первому пользовательскому потоку. Последую­
щие потоки автоматически получают п р и о р и т е т потока, из которого они были
созданы.
Как правило, в реализациях виртуальной машины Java MAX_PRIORITY равен 10,
NORM_PRIORITY- 5, а MIN_PRIORITY — 1. П р и о р и т е т ы потоков Java отображаются
на п р и о р и т е т ы конкретных операционных систем. Учитывая, что в системе Solaris
определено 2 уровней приоритетов, а в Windows N T только 7 уровней, становится
ясно, что в этих системах п р и о р и т е т ы Java будут представлены по-разному. В Windows
NT два приоритета потоков Java могут отображаться на один п р и о р и т е т операцион­
ной системы. Если уровни п р и о р и т е т о в существенно влияют на выполнение про­
граммы, следует тщательно тестировать продукт перед тем, как применять его для
решения конкретных задач.

Методы
p u b l i c static int activeCount()
Данный метод возвращает число активных потоков в группе T h r e a d G r o u p (и всех
подгруппах).

p u b l i c v o i d checkAccessO
Данный метод определяет, имеет ли текущий поток право модификации потока.
Метод c h e c k A c c e s s используется в аплетах и приложениях, реализующих
SecurityManager.

public static native T h r e a d currentThread()


Метод c u r r e n t T h r e a d возвращает ссылку на текущий поток. Заметьте, что дан­
ный метод определен как s t a t i c и может быть вызван не только из объекта
T h r e a d , но и из метода, принадлежащего любому другому объекту.

p u b l i c v o i d destroyO
Данный метод закрывает поток, не выполняя при этом операции по очистке ресур­
сов. Если поток синхронизирован, блокировка продолжает действовать. В JDK 1.2
этот метод не реализован.

public static void dumpStack()


Данный метод выводит стек вызовов для S y s t e m , e r r .

p u b l i c static int enumerate(Thread[ ] g r o u p T h r e a d s )


Данный метод выполняет поиск активных потоков в группе T h r e a d G r o u p , при­
надлежащей текущему исполняемому потоку. Результаты поиска помещаются в
указанный массив. Для определения необходимых размеров массива может быть
использован метод a c t i v e C o u n t .
684 Глава 16. Использование потоков

public ClassLoader getContextClassLoader() [Java 2]


Метод g e t C o n t e x t C l a s s L o a d e r возвращает объект C l a s s L o a d e r , используемый
потоком для загрузки ресурсов и других классов. Если C l a s s L o a d e r не был явно
задан с помощью метода s e t C o n t e x t C l a s s L o a d e r , то C l a s s L o a d e r для текущего
потока совпадает с объектом C l a s s L o a d e r для родительского потока.

public final String g e t N a m e ( )


Данный метод возвращает имя потока.

public final int getPriority()


Метод g e t P r i o r i t y возвращает п р и о р и т е т потока. П р и н ц и п планирования по­
токов с разными приоритетами будет обсуждаться при рассмотрении метода
setPriority.

public final T h r e a d G r o u p getThreadGroupO


Данный метод возвращает группу T h r e a d G r o u p , к которой принадлежит текущий
поток. Каждый поток относится к той или иной группе. Если группа не указана яв­
но, в конструкторе 1сласса T h r e a d , принимается группа родительского потока.

public void interrupt()


При выполнении данного метода достигаются два результата. Во-первых, если в по­
токе выполняется метод j o i n , s l e e p или w a i t , соответствующий метод генерирует
исключение I n t e r r u p t e d E x c e p t i o n . Во-вторых, данный метод устанавливает в по­
токе флаг, распознаваемый методом i s I n t e r r u p t e d . В этом случае поток отвечает
за проверку состояния флага и выполнение требуемых действий. Метод
i n t e r r u p t e d сбрасывает флаг, а метод i s l n t e r r u p t e d не влияет на его состояние.

public static b o o l e a n interrupted()


Данный метод проверяет, был ли выполняемый в данный момент поток прерван
(т.е. установлен ли соответствующий флаг), и сбрасывает флаг, установленный
раньше с помощью метода i n t e r r u p t . Данный метод отличается от
i s l n t e r r u p t e d тем, что последний лишь проверяет наличие флага, но не сбра­
сывает его.

public final native b o o l e a n isAlive()


Данный метод возвращает значение t r u e для выполняемых потоков, а также для
тех, работа которых была приостановлена. Для потоков, в которых был завершен
метод r u n , возвращается значение f a l s e . Вызов i s A l i v e для текущего потока не
имеет смысла, поскольк)' если в потоке может быть вызван какой-либо метод, то
очевидно, что этот поток активен. Метод i s A l i v e вызывается из внешних объек­
тов, которым необходихмо проверить, активен ли поток, ссылка на которых хра­
нится в переменной.

public final b o o l e a n i s D a e m o n ( )
Данный метод позволяет определить, выполняется ли поток в режиме демона. Ес­
ли активны лишь потоки, выполняемые в режиме демона, Java-программа может
16.5. Методы класса Thread 685

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


щем родительскому потоку, однако впоследствии его состояние можно изменить с
помощью метода s e t Daemon. Примером потока, выполняющегося в режиме де­
мона, является система сборки мусора.

public b o o l e a n i s l n t e r r u p t e d ( )
Данный метод проверяет, установлен ли флаг прерывания потока; при этом со­
стояние флага не изменяется. Чтобы сбросить флаг, надо вызвать из метода r u n ,
принадлежащего потоку с установленным флагом, метод i n t e r r u p t e d .

public final void join() throws I n t e r r u p t e d E x c e p t i o n


public final s y n c h r o n i z e d j o i n ( l o n g m i l l i s e c o n d s ) throws I n t e r r u p t e d E x c e p t i o n
public final s y n c h r o n i z e d j o i n ( l o n g m i l l i s e c o n d s , int n a n o s e c o n d s ) throws
InterruptedException
Указанные методы приостанавливают действие потока до истечения указанного
времени либо до завершения потока, в котором был вызван метод j o i n (в этом
случае метод i s A l i v e должен возвращать значение f a l s e ) . Используя метод
j o i n , разработчик может написать программу так, чтобы она дождалась заверше­
ния одного потока, прежде чем создавать новый; при этом нагрузка на централь­
ный процессор, связанная с ожиданием, остается минимальной. Поток никогда не
должен применять метод j o i n по отношению к самому себе, поскольк)' в этом слу­
чае ожидание будет бесконечным. Более сложное поведение потока можно орга­
низовать с помощью методов w a i t и n o t i f y .

public final native void notify()


public final native v o i d notifyAll()
Подобно w a i t , методы n o t i f y и n o t i f y A l l являются методами класса O b j e c t .
Метод n o t i f y "пробуждает", или активизирует, один поток, а метод n o t i f y A l l
пробуждает все потоки, ожидающие снятия блокировки, реализованной с помо­
щью некоторого объекта. Запросы n o t i f y и n o t i f y A l l может передавать только
фрагмент кода, по инициативе которого было заблокировано выполнение мето­
дов (т.е. данные запросы могут исходить только из синхронизированного блока).
Оповещенные потоки возобновят работу только после того, как процесс, сгенери­
ровавший запрос n o t i f y , снимет блокировк)'. Детально эти вопросы будут рас­
смотрены при обсуждении метода w a i t .

public void run()


В данном методе содержатся команды, предназначенные для выполнения в пото­
ке. По завершении метода r u n поток прекращает свое существование. Метод, соз­
дающий поток, не должен непосредственно вызывать r u n ; вместо этого следует
обращаться к методу s t a r t объекта потока.

public v o i d setContextClassLoader(ClassLoader loader) [Java 2]


Данный метод устанавливает загрузчик классов (объект C l a s s L o a d e r ) для потока.
C l a s s L o a d e r определяет способ, которым виртуальная машина Java должна за-
686 Глава 16. Использование потоков

гружать классы и ресурсы, используемые потоком. Если S e c u r i t y M a n a g e r не по­


зволяет изменять используемый объект C l a s s L o a d e r , генерируется исключение
S e c u r i t y E x c e p t i o n . Если C l a s s L o a d e r явно не установлен, используется за­
грузчик классов родительского потока.

public final void s e t D a e m o n ( b o o l e a n b e c o m e D a e m o n )


Дгннъш метод переводит поток в режим демона. Сразу после создания состояние
потока соответствует состоянию родительского потока; метод s e t Daemon позво­
ляет изменить его. Java-программа может быть завершена тогда, когда среди всех
активных потоков нет ни одного, который не находился бы в режиме демона.

public final void s e t N a m e ( S t r i n g t h r e a d N a m e )


Этот метод изменяет имя потока.

public Ппа1 v o i d setPriority(int threadPriority)


Метод s e t P r i o r i t y изменяет приоритет потока; при выполнении потоки с высо­
ким приоритетом имеют преимущество по сравнению с потоками с низким приори­
тетом. В качестве параметра могут быть заданы значения в диапазоне от
Thread.MIN_PRIORITY до Thread.MAX_PRIORITY. П р и создании потока ему при­
сваивается приоритет родительского потока. Приоритет не может быть более высо­
ким, чем MAX_PRIORITY для группы, которой принадлежит поток. П р и назначении
приоритетов соблюдайте осторожность, поскольку в системах с вытесняющей мно­
гозадачностью может создаться ситуация, при которой потоки с низким приорите­
том не получат управления до тех пор, пока поток с высоким приоритетом не будет
завершен, не перейдет в "спящий" режим или в режим ожидания ввода.

public static native void s l e e p ( l o n g m i l l i s e c o n d s ) throws I n t e r r u p t e d E x c e p t i o n


public static native void s l e e p ( l o n g m i l l i s e c o n d s , int n a n o s e c o n d s )
throws I n t e r r u p t e d E x c e p t i o n
Данный метод переводит поток в режим ожидания либо на заданное время, либо
до тех пор, пока поток не будет прерван. Поскольку s l e e p — статический метод,
он может быть использован приложениями без потоков.
Методика профессионалов

Метод Thread, sleep МОЖНО вызвать из любого метода.

public s y n c h r o n i z e d native v o i d start()


Данный метод инициализирует поток, а затем вызывает метод r u n . Если при вы­
зове конструктора метода в качестве параметра t a r g e t (см. выше) было указано
значение n u l l , метод s t a r t вызывает метод r u n объекта T h r e a d . Если же указан
объект R u n n a b l e , s t a r t вызывает метод r u n этого объекта.
Заметьте, что в классе A p p l e t также определен метод s t a r t , который вызывается
после завершения метода i n i t и перед первым вызовом p a i n t . Эти методы следу­
ет различать. Как правило, инициализация потоков осуществляется в методе
s t a r t аплета.
16.5. Методы класса Thread 687

public Ппа1 void wait() throws InterruptedException


public final void wait(long milliseconds) throws InterruptedException
public final void wait(long milliseconds, int nanoseconds)
throws InterruptedException
Данные методы снимают блокировку и приостанавливают текущий поток. Для пе­
резапуска потоков используются методы n o t i f y и n o t i f y A l l . Реально метод
w a i t относится не к классу Thread, а к классу Object, но может быть вызван
только из синхронизированного метода или блока кода. Например:
p u b l i c synchronized void someMethodO {
doSomePreliminaries();
while (!someContinueCondition()) {
try {
/ / Снятие блокировки и приостановка выполнения.
/ / Восстановить работу можно по инициативе других
/ / частей nporpaiviMbi, но при этом необходимо проверять
/ / условия продолжения, чтобы исключить возобновление
/ / и з - з а некорректных действий. ,
wait О ;
} c a t c h ( I n t e r r u p t e d E x c e p t i o n ie) {}
}
continueOperations();
}
Метод w a i t принадлежит объекту, которым помечен синхронизированный блок.
Поэтому если в качестве дескриптора синхронизации используется объект, от­
личный от текущего ( t h i s ) , то вы можете явно вызывать метод w a i t этого объек­
та, как показано в следующем примере:
public void someOtherMethod() {
doSomeUnsynchronizedStuff();
synchronized(someObject) {
doSomePreliminaries() ;
while (!someContinueCondition0) {
try {
someObject.wait0 ;
} catch(InterruptedException ie) {}
}
continueOperations();
}
doSomeMoreUnsynchronizedStuff();
}
public static native void yield()
Если в программе присутствуют два потока одинакового приоритета и ни один из
них не переводится в "спящий" режим либо в режим ожидания ввода, может воз­
никнуть ситуация, при которой управление не будет передаваться от одного пото­
ка другому. Метод y i e l d вызывает передачу управления другому процессу, выпол­
няющемуся с тем же приоритетом. Может показаться, что вопросы квантования
времени лучше оставить "на усмотрение" исполняющей системы. В большинстве
случаев это верно, однако отсутствие требования обязательного квантования яв­
ляется одним из существенных недостатков спецификации потоков. К счастью,
почти во всех реализациях Java 2 квантование осуществляется корректно.
688 Глава 16. Использование потоков

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


в первоначально используемой модели Java завершение потока осуществлялось с
помощью метода s t o p . В последующих реализациях платформы Java данный метод не
рекомендован к применению, поскольку при таком завершении потока блокировка
немедленно снимается, причем часто это происходит в тот момент, когда состояние
программы оказывается неприемлемым для остальных потоков. Кроме того, при ис­
пользовании метода s t o p ресурсы не освобождаются. В настоящее время рекоменду­
ется устанавливать флаг, сигнализирующий о том, что метод run должен быть завер­
шен. Например:
c l a s s ThreadExample implements Runnable {
p r i v a t e boolean runnings-
p u b l i c ThreadExample()
Thread t h r e a d = new T h r e a d ( t h i s ) ;
thread.start() ;
}
p u b l i c void run(){
running = t r u e ;
while (running) {
}
}
public void setRunning(boolean running) {
this.running = running;
}
}

В данном случае, задавая значение f a l s e флага r u n n i n g , вы указываете, что как


только потоку будет выделено время центрального процессора, цикл w h i l e завер­
шится и метод run закончит выполнение. Такой подход применим в тех случаях, ко­
гда метод run выполняется в одном потоке, либо тогда, когда необходимо завершить
выполнение всех потоков с помощью одного флага.
Для подклассов класса Thread каждый экземпляр класса содержит отдельный ме­
тод run. В данной сит}^ации лучше всего включить в состав класса общедоступный ме­
тод, в теле которого устанавливался бы флаг, требующий завершения метода run.
Рассмотрим код класса, приведенный в листинге 16.16. Данный класс содержит обще­
доступный метод s e t S t a t e , с помощью которого задается состояние потока: STOP,
RUN или WAIT. В методе run класса состояние (значение переменной s t a t e ) посто­
янно проверяется и когда оно станет равным STOP (переменная будет модифициро­
вана из тела метода run либо в результате вызова s e t S t a t e ) , цикл w h i l e завершится
и поток прекратит свое существование. С помощью метода s e t S t a t e можно также
перевести поток в "спящее" состояние, в котором он будет находиться до возникно­
вения прерывания ( t h r e a d , i n t e r r u p t ()) либо до вызова метода s e t S t a t e с пара­
метром RUN. Данный подход позволяет независимо управлять различными потоками.
16.5. Методы класса Thread 689

Листинг 16.16-StoppableThread.Java

/** Шаблон программы, в которой состояние потока контролируется


* с помощью флага.
Ч
public class StoppableThread extends Thread {

public static final int STOP = 0


public static final int RUN = 1
public static final int WAIT = 2
private int state = RUN;

/** Общедоступный метод, который позволяет устанавливать


* флаг, приостанавливающий либо полностью останавливающий
* выполнение потока. Проверка состояния потока выполняется
* с помощью метода checkState.
V
public synchronized void setState(int state) {
this.state = state;
if (state==RUN) {
notify 0 ;
}
}

/** Возвращает состояние потока (RUN, STOP, WAIT).


* При возникновении InterruptedException обычно
* меняется состояние либо выполняется другая задача.
V
private synchronized int checkState() {
while (state==WAIT) {
try {
wait ();
} catch (InterruptedException e) { }
}
return state;

/** Пример потока, который выполняется до тех пор, пока


* не будет остановлен по инициативе родительского потока.
V
public void run() {
while (CheckStateО !=STOP) {
690 Глава 16. Использование потоков

Остановка потока в аплете


в классе Applet содержится метод s t o p , который вызывается при смене Web-
страницы либо (в случае использования Netscape) при изменении размеров окна бро­
узера. В некоторых броузерах переход на новую Web-страницу не приводит к автома­
тическому завершению потоков, поэтому в тело метода s t o p аплета желательно
включить код, который устанавливал бы флаги завершения для потоков.
Методика профессионалов
При написании многопотокового аплета надо включить в состав ме­
тода stop код завершения потоков. Перезапустить потоки можно в
теле метода s tart.

16.6. Группы потоков


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

Конструкторы
public ThreadGroup(String groupName)
Данный конструктор создает именованный объект ThreadGroup, принадлежащий
к той же группе, что и поток, из которого вызывался конструктор.

public ThreadGroup(Thr€adGroup parent, String groupName)


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

Методы
public synchronized int activeCount()
Метод a c t i v e C o u n t возвращает число активных потоков, непосредственно либо
опосредованно принадлежащих данной группе на момент вызова метода.

public synchronized int activeGroupCount()


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

public final void checkAccess()


Данный метод позволяет определить, имеет ли поток право модифицировать
гр)Т1пу. Метод checkAccess применяется в аплетах и приложениях, использую­
щих диспетчер SecurityManager, определяемый пользователем.
16.6. Группы потоков 691

public final s y n c h r o n i z e d void destroyO


Если группа не содержит ни одного потока, она, а также все пустые подгруппы мо­
гут быть удалены из родительской группы. Если в группе содержится хотя бы один
поток, генерируется исключение I l l e g a l T h r e a d S t a t e E x c e p t i o n .

public int e n u m e r a t e ( T h r e a d [ ] threads)


public int enumerate(Thread[ ] t h r e a d s , b o o l e a n recurse)
public int e n u m e r a t e ( T h r e a d G r o u p [ ] groups)
public int e n u m e r a t e ( T h r e a d G r o u p [ ] g r o u p s , b o o l e a n recurse)
Данные методы предназначены для копирования ссылок на активные потоки или
группы в массив. Если значение флага r e c u r s e равно t r u e , метод e n u m e r a t e ре­
курсивно обрабатывает дочерние группы.

public final int getMaxPriorityO


Метод g e t M a x P r i o r i t y возвращает максимальный приоритет, который может
быть назначен потоку в группе. Установка максимального приоритета выполняет­
ся с помощью метода s e t M a x P r i o r i t y .

public final String g e t N a m e ( )


Данный метод возвращает имя группы.

public final T h r e a d G r o u p getParent()


Метод g e t P a r e n t позволяет определить родительскую группу. Для первой груп­
пы, созданной в системе, данный метод возвращает значение n u l l .

public final v o i d interrupt()


Данный метод вызывает метод i n t e r r u p t для каждого потока в группе, а также
для всех подгрупп. На работу метода i n t e r r u p t может влиять диспетчер защиты.

public йпа1 b o o l e a n i s D a e m o n ( )
Метод i s Daemon позволяет определить, имеет ли группа статус демона. Если в та­
кой группе нет ни одного потока, она автоматически удаляется.

public final b o o l e a n isDestroyed()


Метод i s D e s t r o y e d позволяет определить, была ли группа удалена. Группа,
имеющая статус демона, автоматически удаляется после завершения последнего
потока в группе или после удаления последней подгруппы. Добавление к удален­
ной группе объектов T h r e a d и T h r e a d G r o u p запрещено.

public s y n c h r o n i z e d v o i d list()
Данный метод выводит информацию обо всех потоках и о подгруппах, содержа­
щихся в группе, в S y s t e m , o u t . Метод l i s t удобен при отладке программ.
692 Глава 16. Использование потоков

public final b o o l e a n p a r e n t O f ( T h r e a d G r o u p d e s c e n d a n t )
Метод p a r e n t O f позволяет определить, является ли группа предком (не обяза­
тельно непосредственной родительской группой) для указанной группы-потомка.

public final v o i d s e t D a e m o n ( b o o l e a n b e c o m e D a e m o n )
Если родительская группа имеет статус демона, он автоматически передается до­
черней группе. Метод s e t Daemon позволяет изменить статус группы.

public final s y n c h r o n i z e d void setMaxPriority(int max)


Данный метод устанавливает максимальный приоритет, который можно присвоить
потоку в группе с помощью метода s e t P r i o r i t y . На состояние потоков, имеющих
более высокий приоритет, данный метод не оказывает влияния. Кроме того, поток
может наследовать приоритет любого уровня от родительского потока.

public void u n c a u g h t E x c e p t i o n ( T h r e a d thread, Throwable error)


Если в программе не предусмотрена обработка события, генерируемого потоком,
вызывается метод u n c a u g h t E x c e p t i o n . По умолчанию этот метод выводит стек
вызовов.

16.7. Многопотоковая графика и двойная


буферизация
Часто потоки применяются при обработке динамически изменяющихся графиче­
ских изображений. П р и этом разработчики программ используют различные подхо­
ды, каждый из которых имеет свои достоинства и недостатки.
• Вывод графики в теле метода paint. Данный подход упрощает работу про­
граммистов, однако обработка часто меняющихся изображений существенно
замедляет работу программы и приводит к возникновению мерцания.
• Реализация изменяющихся фрагментов в виде отдельных компонентов. В
данном случае программа реализуется достаточно просто и мерцание не возни­
кает, но разработчик должен отказаться от применения диспетчера компонов­
ки и размещать компоненты вручную. Кроме того, быстродействие программы
невелико.
• Непосредственный вывод данных за пределами метода paint. В этом случае
программа реализуется просто, мерцание не наблюдается, но изображение те­
ряется при очередном обновлении экрана.
• П е р е о п р е д е л е н и е метода update и реализация инкрементного обновления
в методе paint. Данный подход устраняет мерцание и несколько повышает эф­
фективность программы, однако при этом части изображения не должны пе­
рекрываться.
• Использование двойной буферизации. В этом случае удается добиться мак­
симальной эффективности и не возникает проблем с перекрывающимися изо­
бражениями. Однако двойная буферизация реализуется достаточно сложно и
требует большого объема памяти.
16.7. Многопотоковая графика и двойная буферизация 693

Вывод графики в теле метода paint


Как правило, при выполнении Java-программ устанавливаются характеристики
изображения, после чего вызывается метод r e p a i n t ; он планирует вызов метода
p a i n t , в котором и выполняется рисование. Метод r e p a i n t вызывает метод u p d a t e ,
который очищает экран и вызывает метод p a i n t , передавая ему объект G r a p h i c s .
Метод p a i n t также автоматически вызывается после инициализации аплета, если
часть окна аплета на время пропадает из области видимости, а также при возникно­
вении событий, связанных с изменением размеров окна и расположения компонен­
тов. При вызове метода p a i n t можно указать прямоугольную область, подлежащую
обновлению, однако определить ее достаточно сложно, поэтому обычно в приклад­
ных программах перерисовывается все содержимое окна.
Предположим, например, что вы разрабатываете пользовательский интерфейс
для имитатора, применяемого на флоте. Пиктограммы представляют корабли. Один
или несколько потоков, выполняемых в фоновом режиме, обновляют положение су­
дов и периодически вызывают метод r e p a i n t . Метод p a i n t перебирает в цикле все
объекты и выводит каждый из них в текущей позиции (листинг 16.17).

Листинг 16.17. S h i p S i m u l a t i o n o a v a
import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
p u b l i c c l a s s ShipSimulation extends Applet implements Runnable {
p u b l i c void run() {
Ship s;
for(int i=0; i<ships.length; i++) {
s = ships[i];
s.move(); // Изменение расположения судов*

repaint()
}

p u b l i c void p a i n t ( G r a p h i c s g) {
Ship s;
f o r ( i n t i=0; i < s h i p s . l e n g t h ; i++) {
s = ships[i];
g . d r a w ( s ) ; / / Вывод в текущей позиции.
}
}

Вы также можете инициировать изменение интерфейса по запросу пользователей,


однако при этом все равно приходится перерисовывать все содержимое экрана.
Предположим, например, что вы хотите выводить щелчком мыши некоторые изо­
бражения. В листинге 16.18 приведен код, в котором объект включается в состав век­
тора, а в методе p a i n t объекты, содержащиеся в составе вектора, перерисовываются.
Результат выполнения данного кода показан на рис. 16.1. После щелчка мыши созда­
ется объект S i m p l e C i r c l e и добавляется к вектору c i r c l e s . Затем вызывается ме­
тод r e p a i n t и перерисовывает каждый из объектов S i m p l e C i r c l e .
694 Глава 16. Использование потоков

1 v:'^"^ ш'Ъ ^'"W5j'^"'^^"asf


~3
Drawing Circles

d
Ш1Ж- ^^Ц)^ Computet

Рис. 16.1. Сохраняя результаты работы программы в струк­


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

Листинг 1 6 . 1 8 . D r a w C i r c l e s . j a v a

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
import j a v a . a w t . e v e n t . * ;
import j a v a . u t i l . V e c t o r ;
/** Аплет, который после щелчка мышью отображает в окне
круг небольшого размера.*/
p u b l i c c l a s s DrawCircles extends Applet {
p r i v a t e Vector c i r c l e s ;
/** После щелчка мьшнью создается объект S i m p l e C i r c l e ,
* включается в состав объекта Vector, а система
* оповещается о том, что необходимо обновить
* содержимое окна (метод r e p a i n t вызывает метод u p d a t e ,
* который очищает экран и вызывает метод p a i n t ) .
V
p r i v a t e c l a s s CircleDrawer extends MouseAdapter {
p u b l i c void mousePressed(MouseEvent event) {
circles.addElement(
16.7. Многопотоковая графика и двойная буферизация 695

new SimpleCircle (event. getX () , event. getY () , 25) ) ,


repaint();
}
}
public void initO {
circles = new Vector();
addMouseListener(new CircleDrawer());
setBackground(Color.white);

/** Перебор в цикле доступных объектов SimpleCircle


* и вывод каждого из них.
V
public void paint(Graphics g) {
SimpleCircle circle;
for(int i=0; i<circles.size(); i++) {
circle = (SimpleCircle)circles.elementAt(i);
circle.draw(g);
}

В листинге 16.19 представлен класс S i m p l e C i r c l e , используемый в D r a w C i r c l e s .

Листинг 1 6 . 1 9 . S i m p l e C i r c l e . J a v a

import java.awt.*;
/** Класс, который содержит переменные для хранения
* координат и радиуса, а также метод для рисования (draw)

public class SimpleCircle {


private int x, y, radius;

public SimpleCircle(int x, int y, int radius) {


setX(x);
setY(y);
setRadius(radius);
}
/** С помощью объекта Graphics SimpleCircle
* выводится в текущей позиции.
V
public void draw(Graphics g) {
g.fillOval(x - radius, у - radius,
radius * 2, radius * 2 ) ;
}
public int getXO { return (x); }
public void setX(int x) { this.x = x; }
public int getYO { return (y); }
696 Глава 16. Использование потоков

public void setY(int у) { this.у = у; }


public int getRadiusO { return (radius) ;
public void setRadius(int radius) {
this.radius = radius;

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

Реализация динамической части изображения


в виде отдельного компонента
Обновление содержимого компонентов происходит автоматически, поэтому кон­
тейнер, содержащий компонент, не должен явно выполнять действия по перерисов­
ке. Например, в рассмотренном выше имитаторе модели кораблей могут быть реали­
зованы как подклассы класса C a n v a s и помещены в окно, с которым не связан ни
один диспетчер компоновки. Для того чтобы изменить позицию судна, управляющий
процесс имитатора должен вызвать метод move компонента. Аналогично, рисование
под управлением событий, инициируемых пользователем, сводится к созданию ком­
понента, установке координат х и у и добавлению компонента к текущему контейне­
ру. П р и м е р кода, осуществляющего рисование кругов, был рассмотрен в главе 13.

Преимущества и недостатки
Иногда реализовать данный подход оказывается проще, чем выполнять рисование
непосредственно в теле метода p a i n t , поскольку при этом перемещением управляет
непосредственно компонент. В код контейнера вносятся лишь незначительные изме­
нения. Однако в данном случае возникают проблемы с перекрывающимися компо­
нентами, кроме того, быстродействие оказывается низким и используются большие
объемы памяти. Создание отдельного объекта C a n v a s для каждого элемента рисунка
требует большого объема ресурсов, а перемещение компонентов не сводится к обыч­
ной перерисовке.

Рисование за пределами метода paint


в некоторых случаях разработчики вовсе не вызывают метод p a i n t . Вместо этого
они выполняют рисование из других методов. Чтобы сделать это, надо получить объ­
ект G r a p h i c s , установить режим рисования с использованием логической функции
XOR и вызывать методы drawXxx объекта G r a p h i c s . Для того чтобы стереть нари-
16.7. Многопотоковая графика и двойная буферизация 697

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

Листинг 16.20.Rubberband.Java

import Java.applet.Applet;
import java.awt.*;
import Java.awt.event.*;

/•k-k Рисование прямоугольников с помощью мыши. */

public class Rubberband extends Applet {


private int startX, startY, lastX, lastY;

public void init() {


addMouseListener(new RectRecorder());
addMouseMotionListener(new RectDrawer());
setBackground(Color.white);

/•• Рисование прямоугольника. Значения x, у, w, h


* вычисляются по координатам начальной и конечной точек.
V
private void drawRectangle(Graphics g, int startX, int startY,
int stopX, int stopY ) {
int X, y, w^ h;
X = Math.min(startX, stopX);
у = Math.min(startY, stopY);
w = Math.abs(startX - stopX);
h = Math.abs(StartY - stopY);
g.drawRect(x, y, w, h ) ;
}
private class RectRecorder extends MouseAdapter {

/** После нажатия кнопки мыши записываются


* координаты верхней левой точки прямоугольника.
V
public void mousePressed(MouseEvent event) {
StartX = event.getX0;
StartY = event.getY0;
lastX = StartX;
lastY = StartY;
}

/** При отпускании кнопки мыши прямоугольник стирается,

public void mouseReleased(MouseEvent event) {


698 Глава 16. Использование потоков

Graphics g = getGraphics();
g.setXORMode(Color.lightGray) ;
drawRectangle(g, startX, startY, lastX, lastY);

}
private class RectDrawer extends MouseMotionAdapter {
/** Прямоугольник определяется по координатам точки,
'^ в которой пользователь нажал кнопку мыши, и той
* точки, куда он переместил курсор.

public void mouseDragged(MouseEvent event) {


int X = event.getX();
int у = event.getY0;
Graphics g = getGraphics();
g.setXORMode(Color.lightGray);
drawRectangle(g, startX, startY, lastX, lastY);
drawRectangle(g, startX, startY, x, y ) ;

lastX = x;
lastY = y;

£te £ d i Унт £o £о(ш»ип»й«йог HetP

13
Click and drag to show rubberbanding

Рис. 16.2. Java позволяет непосредст­


венно выполнять рисование из потоков
и из обработчиков событий, однако такие
iifacf •AR^Ri4*e^»n<JlM»ww^ ; Ш..^Л^..Ш „>2^J изображения существуют лишь временно

Достоинства и недостатки
Данный подход (непосредственное рисование вместо установки значений пере­
менных и вывода изображений в теле метода p a i n t ) хорошо подходит для получения
результатов, используемых в течение ограниченного интервала времени. Как только
метод p a i n t очередной раз получит управление, картинка на экране будет стерта.
Таким способом невозможно создавать изображения, которые должны постоянно
присутствовать в окне программы.
1 6 . 7 . Многопотоковая графика и двойная буферизация 699

Переопределение метода update и использование


метода paint для инкрементного рисования
Предположим, что некоторый графический объект должен перемещаться в пре­
делах экрана. В этом случае не обязательно очищать экран и полностью перерисовы­
вать изображение; метод p a i n t можно использовать для того, чтобы стереть текущий
объект (например, отобразив прямоугольник, заполненный цветом фона) и вывести
этот объект в новой позиции. При этом время на перерисовку остальной части окна
не затрачивается. Метод p a i n t может быть вызван из тела метода, обрабатывающего
событие, связанное с клавиатурой и мышью, либо из фонового потока, в котором
присутствует обращение к методу r e p a i n t . Чтобы при использовании данного спо­
соба не возникало мерцание экрана, необходимо переопределить метод u p d a t e , ко­
торый перед вызовом метода p a i n t не очищал бы экран.
public void update(Graphics g) {
paint(g);
}

Поскольку метод p a i n t часто вызывается из того потока, в котором осуществля­


ется обработка событий, связанных с мышью и клавиатурой, очень важно, чтобы вы­
полнение этого метода занимало как можно меньше времени. Как вы помните, во
время выполнения метода p a i n t кнопки, полосы прокрутки и прочие компоненты не
реагируют на действия пользователя.
Методика профессионалов

Время, требуемое для выполнения метода paint, необходимо ми­


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

В листинге 16.21 показан код аплета, иллюстрирующего данный подход. Приве­


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

Листинг 16.21.Bounce.Java

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
import J a v a . a w t . e v e n t . * ;
import j a v a . u t i l . V e c t o r ;
/** Перемещение кругов по экрану. Двойная буферизация
* не используется, вследствие чего возникают проблемы
* с перекрывающимися кругами. Метод update переопределен,
* чтобы исключить мерцание.
V
public c l a s s Bounce extends Applet implements Runnable,
ActionListener {
p r i v a t e Vector c i r c l e s ;
700 Глава 16. Использование потоков

private int width, height;


private Button startButton, stopButton;
private Thread animationThread = null;
public void initO {
setBackground{Color.white) ;
width = getSize0 .width;
height = getSize().height;
circles = new Vector ();
StartButton = new Button("Start a circle");
StartButton.addActionListener(this);
add(startButton) ;
StopButton = new Button("Stop all circles");
StopButton.addActionListener(this);
add(stopButton) ;
}
/** Движение начинается после щелчка на кнопке
* "St^rt а circle", при этом создается анимационный
'^ поток (если он еще не существует) . Очередной круг
* добавляется к объекту Vector.
* <Р>
* После щелчка на кнопке "Stop all circles" анимационный
* поток останавливается и очищается вектор объектов.
V
public void actionPerformed(ActionEvent event) {
if (event.getSource() == startButton) {
if (circles.size() == 0) {
// Стирание кругов, соответствующих
// предыдущему запуску.
getOraphics().clearRect(О, О, getSize().width,
getSize().height);
animationThread = new Thread(this);
animationThread.start() ;
}
int radius = 25;
int X = radius + randomint(width - 2 * radius);
int у = radius + randomint(height - 2 * radius);
int deltaX = 1 + randomint(10);
int deltaY = 1 + randomint(10);
circles.addElement(new MovingCircle(x, y, radius, deltaX,
deltaY));
} else if (event.getSource() == stopButton) {
if (animationThread != null) {
animationThread = null;
circles.removeAllElements();
}

repaint 0 ;
}

/•• g теле цикла вызывается метод paint, а затем программа


* делает небольщую паузу. Метод paint перемещает
* круги и отображает их.
V
public void run() {
16.7. Многопотоковая графика и двойная буферизация 701

Thread myThread = Thread.currentThread();


// На самом деле, пока animationThread не равно null
while(animationThread==myThread) {
repaint();
pause(100);
}

/** Из метода update удалена операция очистки экрана,


* благодаря чему устраняется мерцание.

public void update(Graphics g) {


paint(g);
}

/** Удаление круга в предыдущей позиции, перемещение его


* и рисование в новом положении.

public void paint(Graphics g) {


MovingCircle circle;
for(int i=0; i<circles.size(); i++) {
circle = (MovingCircle)circles.elementAt(i);
g.setColor(getBackground());
circle.draw(g); // Старая позиция,
circle.move(width, height);
g.setColor(getForeground());
circle.draw(g); // Новая позиция.
}
}

// Возвращает целочисленные значения О to max


// (включительно), реализуя шах + 1 случайных чисел.

private int randomint(int max) {


double X =
Math, floor ( (double) (max -»- 1) * Math. random() ) ;
return((int)(Math.round(x)));

// Перевод в "спящий" режим на указанное время.

private void.pause(int milliseconds) {


try {
Thread.sleep((long)milliseconds);
} catch(InterruptedException ie) {}
702 Глава 16. Использование потоков

Fife £ctt )tttm Favc#e« Jools 1|ф B l

1 A
Bouncing Circles

• •

iA
• • • • ^ *

J
^ АррЫ tidfted ' ' [3''МуСоМ{ри(в1

Рис. 16.3. Инкрементное рисование, выполняемое в теле метода paint,


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

Класс Bounce использует класс MovingCircle, в котором инкапсулировано пере­


мещение круга; позиция и размеры унаследованы от класса S i m p l e C i r c l e . Код клас­
са MovingCircle показан в листинге 16.22.

Листинг 16.22. MovingCircle. java

/** Расширение S i m p l e C i r c l e ; объект может перемещаться на


* расстояние, соответствующее значениям deltaX и d e l t a Y .
* Объект двигается в одном направлении до тех пор, пока не
* достигнет края окна, "отскакивает" от него и двигается
* в противоположном направлении.

public class MovingCircle extends SimpleCircle {


private int deltaX, deltaY;

public MovingCircle(int x, int y, int radius, int deltaX,


int deltaY) {
super(x, y, radius);
this.deltaX = deltaX;
this.deltaY = deltaY;
}
16.7. Многопотоковая графика и двойная буферизация 703

public void move(int windowWidth, int windowHeight) {


setX(getX() + getDeltaX());
setY(getY() + getDeltaY());
bounce(windowWidth, windowHeight);
}
private void bounce(int windowWidth, int windowHeight) {
int X = g e t X O , у = g e t Y O , radius = getRadius () ,
deltaX = getDeltaX0, deltaY = getDeltaY();
if ((X - radius < 0) && (deltaX < 0)) {
setDeltaX(-deltaX);
} else if ((X + radius > windowWidth) && (deltaX > 0)) {
setDeltaX(-deltaX);
}
if ((y -radius < 0) && (deltaY < 0)) {
setDeltaY(-deltaY);
} else if((y + radius > windowHeight) && (deltaY > 0)) {
setDeltaY(-deltaY);
}
}

public int getDeltaX0 {


return(deltaX);
}

public void setDeltaX(int deltaX) {


this.deltaX = deltaX;
}
public int getDeltaY0 {
return(deltaY);

public void setDeltaY(int deltaY)


this.deltaY = deltaY;
}
}

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

Использование ДВОЙНОЙ буферизации


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

буферизация. При двойной буферизации создается внеэкранное изображение (карта


пикселей), над которым выполняются все операции рисования. Как и раньше, метод
u p d a t e должен быть переопределен так, чтобы отменить операцию очистки экрана.
Сделать это необходимо; несмотря на то, что изображение полностью перерисовыва­
ется, реализация u p d a t e по умолчанию перед выводом изображения стирает преды­
дущее содержимое окна, заполняя его цветом фона.
Поддержка двойной буферизации встроена в Swing-компоненты, однако для AWT-
компонентов ее должен обеспечить разработчик. Несмотря на то что при решении
различных задач двойная буферизация может быть реализована по-разному, необхо­
димо выполнить описанные ниже действия.
1. П е р е о п р е д е л е н и е метода update. В переопределенном методе должен содер­
жаться только вызов метода p a i n t . Это позволяет избежать мерцания, которое
неизбежно появлялось бы в результате очистки экрана перед вызовом p a i n t .
2. Вызов метода createlmage. Поскольку для вывода изображения необходима
поддержка окна, созданного средствами платформы, разместить изображение
невозможно до тех пор, пока не будет создано соответствуюш[ее окно. Для ап-
летов это означает, что вызов c r e a t e l m a g e можно включать в тело метода
i n i t (или методов, выполняющихся позже), но нельзя осуществлять непосред­
ственную инициализацию переменной экземпляра. В приложениях метод
c r e a t e l m a g e можно вызывать после того, как главное окно будет создано и
выведено на экран (например, с помощью метода s e t V i s i b l e ) . П р и вызове
c r e a t e l m a g e для компонентов, для которых отсутствуют платформенно-
ориентированные аналоги, возвращается значение n u l l . В обычных условиях
платформенно-ориентированный элемент создается тогда, когда компонент
отображается впервые. Ч т о б ы элемент был создан перед первым вызовом
s e t V i s i b l e , можно использовать метод a d d N o t i f у.

Внимание!

При вызове createlmage для компонента, который еще не стал ви­


димым, возвращается значение null.

3. Пол)^ение графического объекта с помощью метода getGraphics. В отличие от


рисования в окне, где каждый раз необходимо получать контекст G r a p h i c s , при
работе с изображением вы можете единожды связать с ним объект G r a p h i c s , со­
хранить ссылку на объект в переменной, а затем повторно использовать его.
4. Очистка изобралсения и перерисовка объектов. Эти операции выполняются
значительно быстрее, чем соответствующие действия при рисовании в окне.
П р и использовании Swing необходимые результаты можно получить, вызвав
super.paintComponent.
5. Отобралсение внеэкранного изображения в окне. Для этого используется
метод d r a w l m a g e .
В листинге 16.23 приведен код, непосредственно реализующий описанных подход.
В аплете DoubleBuf f е г В о и п с е , в отличие от рассмотренного ранее Bounce, использу­
ется двойная буферизация, что повышает производительность и устраняет проблемы с
перекрывающимися кругами. Результаты выполнения кода показаны на рис. 16.4.
16.7. Многопотоковая графика и двойная буферизация 705

Листинг 16.23.DoubleBufferBounce.Java

import Java, applet .Applets-


import java.awt.*;
import Java.awt.event.*;
import java.util.Vector;
/** Перемещение кругов по экрану с использованием двойной
* буферизации; при этом ускоряется работа и исчезают
* проблемы с перекрывающимися кругами.
* Для устранения мерцания метод update переопределяется.
V
public class DoubleBufferBounce extends Applet implements
Runnable, ActionListener
private Vector circles;
private int width, height;
private Image offScreenlmage;
private Graphics offScreenGraphics;
private Button startButton, stopButton;
private Thread animationThread = null;

public void initO {


setBackground(Color.white);
width = getSize0.width;
height = getSize().height;
offScreenlmage = createlmage(width, height);
offScreenGraphics = offScreenlmage.getGraphics();
// В некоторых системах выполняется автоматически.
offScreenGraphics.setColor(Color.black);
circles = new Vector();
StartButton = new Button("Start a circle");
StartButton.addActionListener(this);
add(StartButton);
StopButton = new Button("Stop all circles");
StopButton.addActionListener(this);
add(stopButton);
}
/** Движение начинается после щелчка на кнопке
* "Start а circle", при этом создается анимационный
* поток (если он еще не существует). Очередной круг
* добавляется к объекту Vector.
•к <р>
* После щелчка на кнопке "Stop all circles" анимационный
* поток останавливается и очищается вектор объектов.
*/
public void actionPerformed(ActionEvent event) {
if (event.getSource() == startButton) {
if (circles.size() == 0) {
animationThread = new Thread(this);
animationThread.start 0 ;
}
int radius = 25;
int X = radius + randomint(width - 2 * radius);
706 Глава 16. Использование потоков

int у = radius + randomint(height - 2 * radius);


int deltaX = 1 + randomint(10);
int deltaY = 1 -f randomint (10) ;
circles.addElement(new MovingCircle(x, y, radius, deltaX,
deltaY));
repaint();
} else if (event.getSource() == stopButton) {
if (animationThread != null) {
animationThread = null;
circles.removeAllElements() ;

/** При очередной итерации цикла каждый круг перемещается


* на величину, определяемую deltaX/deltaY. При достижении
* края окна соответствующее значение изменяется на обратное.
*/
public void run () {
MovingCircle circle;
Thread myThread = Thread.currentThread();
// Ha самом деле, пока animationThread не равно null,
while(animationThread==myThread) {
for(int j=0; j<circles.size(); j++) {
circle = (MovingCircle)circles.elementAt(j);
circle.move(width, height);
}
repaint();
pause(100);
}
}
/** Из метода update исключена операция очистки экрана,
* благодаря чему устраняется мерцание.

public void update(Graphics g) {


paint(g);
}
/** Очистка внеэкранной карты пикселей, рисование каждого
* круга и вывод внеэкранного изображения в окно аплета.

public void paint(Graphics g) {


offScreenGraphics.clearRect(0, 0, width, height);
MovingCircle circle;
for(int i=0; i<circles.size() ; i++) {
circle = (MovingCircle)circles.elementAt(i);
circle.draw(offScreenGraphics);
}
g.drawImage(offScreenlmage, 0, 0, this);
}
// Возвращает целочисленные значения от О до max
// (включительно), реализуя max + 1 возможных значений.
16.7. Многопотоковая графика и двойная буферизация 707

private int randomint(int шах) {


double X = Math.floor((double)(max + 1 ) * Math.random());
return((int)(Math.round(x)));
}
// Перевод в "спящий" режим на указанное время.
private void pause(int milliseconds) {
try {
Thread.sleep((long)milliseconds);
} catch(InterruptedException ie) {}
}

mm
P e £<i Wm §0 £c(imtnic^o( ii(^

й ^' a .^ ^ iai-**аш
Bouncing Circles
!шшшit CHoodilditiss
1 1
ф %
• •

'
%

'
фф

4^Й»«Г
• • ;n ^jhLk^^^
1
Ш , , v<g^„„

Рис. 16.4. Применение двойной буферизации обеспечивает вы­


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

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

Однако не следует недооценивать достоинства двойной буферизации. Для реали­


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

16.8. Анимационные изображения


Потоки Java как нельзя лучше подходят для отображения анимационных последо­
вательностей. Многие Web-страницы содержат анимационные GIF-файлы (в формате
GIF89A) и выводят в цикле набор изображений. Обычно анимационные GIF-файлы
создаются с помощью специальных графических пакетов, например Adobe PhotoShop
или Quark, которые объединяют несколько изображений одинакового размера в один
GIF-файл. Будучи загруженными в Web-броузер, каждое изображение GIF-последо­
вательности отображается в течение заданного интервала времени. Язык Java обес­
печивает более высокий уровень контроля над выводом изображений, представлен­
ных в формате GIF89A. П р и отображении анимационных последовательностей вы­
полняются перечисленные ниже действия.
• Ч т е н и е последовательности изображений в массив Image.
• Определение переменной для перебора элементов массива Image.
• Запуск потока, в котором перебирается последовательность индексированных
значений, и для каждого элемента, на который указывает переменная, вызыва­
ются методы r e p a i n t и s l e e p .
• Вывод изображения в методе p a i n t (AWT) или p a i n t C o m p o n e n t (Swing) с по­
мощью объектов G r a p h i c s и G r a p h i c s 2 D .
Порядок, в котором изображения представляются пользователю, зависит от зна­
чений индексов, а интервал между выводом изображений, составляющих последова­
тельность, определяется временем, в течение которого поток находится в "спящем"
режиме. Подробно о процессе загрузки и вывода изображений см. в главе 9.
П р и м е р создания анимационных изображений показан в листинге 16.24. В этом
аплете каждый объект Duke представляет отдельный поток, в котором последова­
тельно перебираются элементы массива, содержащего 15 изображений. В одном из
потоков значения индекса возрастают ( t u m b l e D i r e c t i o n равно 1), а в другом — убы­
вают ( t u m b l e D i r e c t i o n равно - 1 ) . Каждый из потоков должен иметь доступ к методу
r e p a i n t аплета, поэтому конструктору Duke передается ссылка на родительский
компонент A p p l e t . После изменения индекса в потоке вызывается метод r e p a i n t ,
вследствие чего содержимое области аплета перерисовывается. Метод p a i n t обра­
щается к каждому из потоков и получает значение индекса, после чего выводит соот­
ветствующее изображение.
Кроме того, в классе Duke содержится общедоступный метод s e t S t a t e , с помо­
щью которого поток запускается при загрузке HTML-страницы в броузер и останав­
ливается при прекращении отображения страницы. Предварительно загружая изо­
бражения, предназначенные для вывода, можно повысить качество анимации. Под­
робно о предварительной загрузке изображений см. в главе 9.
16.8. Анимационные изображения 709

Листинг 16.24. ImageAnimation. Java

import Java.applet.Applet;
import java.awt.*;

public class ImageAnimation extends Applet {

/** В массиве содержится анимационная последовательность из


* 15 изображений. Каждый из объектов Duke управляется
* отдельным потоком. Метод stop аплета завершает выполнение
* потока, вызывая общедоступный метод setState класса Duke.
* Метод update переопределен для устранения мерцания.
Ч
private static final int NUMDUKES = 2;
private Duke[] dukes;
private int i;

public void initO {


dukes = new Duke[NUMDUKES];
setBackground(Color.white);
}

/** Запуск каждого потока с указанием направления перебора


* элементов массива изображений.

public void start О {


int tumbleDirection;
for (int i=0; KNUMDUKES ; i++) {
tumbleDirection = (i%2==0) ? 1 :-l;
dukes[i] = new Duke(tumbleDirection, this);
dukes[i].start();
}
}

/** Из метода update исключена операция очистки экрана,


* благодаря чему устраняется мерцание.

public void update(Graphics g) {


paint(g);
}
public void paint(Graphics g) {
for (i=0 ; KNUMDUKES ; 1++) {
if (dukes[i] != null) {
g.drawlmage(Duke.images[dukes[i].getlndex()],
200*1, 0, this);
}
}
}
710 Глава 16. Использование потоков

/•• Метод stop аплета обращается к методу setState класса


* Duke, в котором устанавливается флаг, предназначенный
* для завершения метода run.
V
public void stop О {
for (int i=0; i<NUMDUKES ; i++) {
if (dukes[i] != null) {
dukes[i].setState(Duke.STOP);
}
}
}

Листинг 16.25.Duke.Java

import Java.applet.Applet;
import java.awt.*;

/** Класс Duke является подклассом класса Thread. При вызове


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

public class Duke extends Thread {


public static final int STOP = 0;
public static final int RUN = 1;
public static final int WAIT = 2;
public static Image[] images;
private static final int NUMIMAGES = 15;
private static Object lock = new Object();
private int state = RUN;
private int tumbleDirection;
private int index = 0;
private Applet parent;

public Duke(int tumbleDirection, Applet parent) {


this.tumbleDirection = tumbleDirection;
this.parent = parent;
synchronized(lock) {
if (images==null) { // Если не загружен ранее,
images = new Image[ NUMIMAGES ];
for (int i=0; KNUMIMAGES; i++) {
images[i] = parent.getlmage( parent.getCodeBase(),
"images/T" + i + ".gif");
}
}
}

/** Возвращает текущий индекс массива изображений.


16.8. Анимационные изображения 711

public int getlndexO { return index; }


/** Общедоступный метод, позволяющий устанавливать флаг,
* предназначенный для остановки потока. Для управления
* состоянием служит метод checkState.
V
public synchronized void setState(int state) {
this.state = state;
if (state==RUN) {
notify 0 ;
}
}
/** Возвращает состояние потока (RUN, STOP, WAIT).
* Если поток приостановлен, метод wait вызывается
* до тех пор, пока состояние не будет изменено
* посредством метода setState.
V
private synchronized int checkState() {
while (state==WAIT) {
try {
wait 0 ;
} catch (InterruptedException e) {}
}
return state;
}
/** В каждой итерации увеличивается значение переменной
* index (содержащей индекс массива), после чего вызывается
*^ метод repaint и выдерживается пауза. Кроме того, в
* цикле проверяется значение флага состояния.
V
public void run() {
while (CheckStateО !=STOP) {
index += tumbleDirection;
if (index < 0) {
index = NUMIMAGES - 1;
}
if (index >= NUMIMAGES) {
index = 0;
}
parent.repaint();

try {
Thread.sleep(100);
} catch (InterruptedException e) {
break; // Выход из цикла while.
}
712 Глава 16. Использование потоков

ьч.|'''1й4'Л1'И11|Ц'^|1шша
/ ^
1 4f} Рис. 16.5. Использование потоков для отображения
Applet started. анимационных последовательностей объектов Duke

Java также поддерживает ф о р м а т GIF89A. Для этого надо загрузить GIF-файл в


объект Image и, как обычно, вывести изображение в теле метода p a i n t . Java создает
отдельный поток A n i m a t o r , в котором последовательно вызывается метод r e p a i n t
и упорядочиваются изображения, входящие в состав файла GIF89A. Со временем
пользователь прекратит работу с документом, содержащим аплет, в который поме­
щен файл GIF89A, и перейдет к другой Web-странице. Как, спросите вы, прекратить
работу потока, управляющего выводом анимационной последовательности? Какой
флаг надо установить для завершения соответствующих методов r u n ? В Internet Ex­
plorer поток A n i m a t o r автоматически завершается при переходе на новую Web-
страницу. К сожалению, в Netscape для этого надо предпринимать специальные меры.

16.9. Таймеры
Таймеры используются для решения самых разнообразных задач, например для
отображения анимационных последовательностей, запуска и остановки программ по
расписанию и организации тайм-аута при установке сетевых соединений. В предыду­
щем разделе был продемонстрирован вывод анимационных изображений с помощью
отдельных потоков. В потоке периодически выполнялись определенные действия,
после чего поток на некоторое время переводился в "спящий" режим. Подобные за­
дачи решаются проще при использовании класса j a v a x . s w i n g . T i m e r , который по­
зволяет легко организовать цикл, осуществляющий периодическое выполнение тре­
буемых действий.
Создание таймера сводится к построению экземпляра класса T i m e r . Конструктору
класса передаются значение интервала времени в миллисекундах и объект
A c t i o n E v e n t , определяющий обработчик событий. После создания таймера он дол­
жен быть активизирован с помощью метода s t a r t . Например:
Timer t i m e r = new T i m e r ( p e r i o d , listener);
timer.start() ;
По умолчанию объект T i m e r создает поток, который через интервал времени
(в миллисекундах), заданный с помощью параметра p e r i o d , передает всем обработ­
чикам (см. a d d A c t i o n L i s t e n e r ) событие A c t i o n E v e n t . Объект T i m e r может быть
настроен так, что через указанный промежуток времени он сгенерирует только одно
событие, а затем прекратит свою работу. Ч т о б ы перевести T i m e r в такой режим, на­
до вызвать метод s e t R e p e a t s ( f a l s e ) . В отличие от обычного объекта T h r e a d , ко­
торый не допускает повторного запуска потока после остановки. T i m e r позволяет
перезапустить поток с помощью метода r e s t a r t .
Событие, сгенерированное объектом T i m e r , помещается в очередь событий.
Во время работы программы может возникнуть ситуация, при которой до обработки
первого события T i m e r , содержащегося в очереди, объект сгенерирует еще несколь-
16.9. Таймеры 713

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


очереди присутствует более раннее событие Timer, текущее событие не будет поме­
щено в очередь. Если же необходимо, чтобы обрабатывалось каждое событие, надо
отменить объединение, вызвав метод s e t C o a l e s e ( f a l s e ) . Если вы разрешите про­
токолирование действий таймера, вызвав для этого метод setLogTimers ( t r u e ) , ка­
ждый раз, когда таймер сгенерирует событие ActionEvent, в стандартный выходной
поток будут выведено соответствующее сообщение. Это дает возможность контроли­
ровать поведение таймера. Метод setLogTimers объявлен как s t a t i c , поэтому со­
общения будут выводиться всеми таймерами, активными в данной программе.
Использование объекта Timer ддя управления анимационными последовательно­
стями показано в листингах 16.26 и 16.27. Применение таймеров существенно упро­
щает задачу, поскольку в этом случае больше не требуется непосредственно создавать
объекты Thread и управлять ими. Интервал времени между событиями различается
для разных таймеров. В конструкторе TimedDuke присутствуют синхронизирован­
ный блок и флаг loaded; их использование предотвращает возникновение "гонок".
После того как MediaTracker подтвердит загрузку изображений в массив, аплет за­
пускает таймеры. При возникновении события вызывается метод a c t ionPe г formed
соответствующего объекта TimedDuke, в котором инкрементируется индекс массива
изображений и вызывается метод r e p a i n t аплета. В теле методов s t a r t и s t o p ап-
лета осуществляются соответственно запуск и остановка таймера.

Листинг 16.26, TimedAnimation. Java

import j a v a . a w t . * ;
import j avax.swing.*;
/** Пример поддержки анимации с применением таймеров.
* Два объекта Dukes используют таймеры с различными
* периодами возникновения событий.
V
p u b l i c c l a s s TimedAnimation extends JApplet {
p r i v a t e s t a t i c f i n a l i n t NUMDUKES = 2;
p r i v a t e TimedDuke[] dukes;
p r i v a t e i n t i , index;
p u b l i c void i n i t O {
dukes = new TimedDuke[NUMDUKES];
setBackground(Color.white);
dukesEO] = new TimedDuke( 1, 100, t h i s ) ;
dukes[1] = new TimedDuke(-1, 500, t h i s ) ;

// Запуск таймера для каждого Duke.


p u b l i c void s t a r t 0 {
for ( i n t i=0; KNUMDUKES ; i++) {
dukes[i].startTimer();
}

public void paint(Graphics g) {


714 Глава 16. Использование потоков

for (i=0 ; KNUMDUKES ; i++) {


if (dukes[i] != null) {
index = dukes[i].getlndex0;
g.drawlmage(TimedDuke.images[index], 200*i, 0, this);
}
}
}
// Остановка таймера для каждого Duke.

public void stopO {


for (int i=0; KNUMDUKES ; i++) {
dukes [i] .stopTimerO ;
}
}
}

Листинг 16.27. TimedDuke. Java

import Java.applet.Applet;
import java.awt.*;
import Java.awt.event.*;
import j avax.swing.*;

/** Duke поддерживает анимацию с помощью


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

public class TimedDuke implements ActionListener {


private static final int NUMIMAGES = 15;
private static boolean loaded = falser-
private static Object lock = new Object ();
private int tumbleDirection;
private int msec;
private int index = 0;
private Applet parent;
private Timer timer;
public static Image[] images = new Image[NUMIMAGES];

public TimedDuke(int tumbleDirection, int msec.


Applet parent) {
this.tumbleDirection = tumbleDirection;
this.msec = msec;
this.parent = parent;
synchronized (lock) {
if (!loaded) {
MediaTracker tracker = new MediaTracker(parent);
for (int i=0; KNUMIMAGES; i++) {
images[i] = parent.getlmage(parent.getCodeBase(),
"images/T" + i + ".gif");
16.9. Таймеры 715

tracker.addlmage(images[i] , 0) ;
}
try {
tracker.waitForAll();
} catch (InterruptedException ie) {}
if (!tracker.isErrorAny0) {
loaded = true;
}
}
}
timer = new Timer(msec, this);
}
// Возвращает текущее значение индекса.
public int getlndexO { return index; }
// Поддержка события таймера. Метод actionPerformed
// увеличивает значение индекса массива изображений
/ / и инициирует вывод нового изображения.

public void actionPerformed(ActionEvent event) {


index += tumbleDirection;
if (index < 0){
index = NUMIMAGES - 1;
}
if (index >= NUMIMAGES) {
index = 0;
}
parent.repaint();
}
// Вспомогательный метод, предназначенный для запуска таймера,
public void startTimerO {
timer.start();
}
// Вспомогательный метод, предназначенный для остановки таймера,
public void stopTimerO {
timer.stop();
}

Конструктор
в классе T i m e r предусмотрен только один конструктор.

public Timer(int p e r i o d , A c t i o n L i s t e n e r listener)


Данный конструктор позволяет установить интервал времени и задать обработ­
чик, которому должны передаваться события. События доставляются всем зареги­
стрированным обработчикам.
71 б Глава 16. Использование потоков

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


public void addActionListener(ActionListener listener)
Данный метод связывает обработчик A c t i o n L i s t e n e r с объектом Timer. Собы­
тие ActionEvent, помещенное в очередь, доставляется всем зарегистрированным
обработчикам и приводит к вызову в каждом из них метода a c t i o n P e r f ormed.

public boolean isRunningO


Данный метод возвращает значение t r u e , если таймер активен; в противном слу­
чае возвращается значение f a l s e .

public void removeActionListener(ActionListener listener)


Данный метод разрывает связь указанного обработчика A c t i o n L i s t e n e r с объек­
том Timer.

public void restartO


Метод r e s t a r t удаляет все необработанные события и перезапускает таймер.

public void setCoalesce(boolean flag)


Метод s e t C o a l e s c e разрешает ( t r u e ) или запрещает ( f a l s e ) объединение со­
бытий ActionEvent. По умолчанию, если в очереди есть необработанное собы­
тие ActionEvent, сгенерированное таймером, то новое подобное событие в оче­
редь не помещается. Если вы отключите объединение событий, будет обрабаты­
ваться каждое из ActionEvent.

public void setDelay(int period)


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

public void setInitialDelay(int delay)


Метод s e t l n i t i a l D e l a y задает начальную задержку (в миллисекундах) перед ге­
нерацией таймером первого события. Если работа таймера началась, данный ме­
тод не оказывает никакого влияния на его действия.

public static void setLogTimers(boolean flag)


Данный статический метод разрешает или запрещает протоколирование событий,
генерируемых всеми таймерами. Сообщение о событии выводится в System, out в
следующем формате:
Timer r i n g i n g : TimedDuke@3f345а.

public void setRepeats(boolen repeat)


Метод s e t R e p e a t s определяет, должен ли таймер генерировать событие едино­
жды (значение параметра f a l s e ) или многократно (значение параметра t r u e ) .
По умолчанию многократные события разрешены.
16.10. Резюме 717

public void start()


Метод s t a r t запускает таймер. Если прежде таймер был остановлен с помощью
метода s t o p , он начинает работу в текущем состоянии; значение времени не обну­
ляется.

public void stopO


Метод s t o p останавливает таймер; после вызова этого метода объект Timer пре­
кращает генерировать события ActionEvent.

16.10. Резюме
Потоки могут использоваться для решения самых разнообразных задач и иногда
упрощают написание программ. Встречаются ситуации, в которых разбиение про­
граммы на нескольких независимо выполняемых частей является более органичным
решением, чем координация различных действий в рамках одной процедуры. В дру­
гих случаях использование потоков позволяет повысить эффективность работы про­
граммы; примером может служить установление нескольких сетевых соединений.
Следует помнить, что при разработке многопотоковых программ приходится за­
трачивать гораздо больше усилий, а отладка их происходит гораздо сложнее по срав­
нению с однопотоковыми программами. Поэтому, принимая решение об использова­
нии потоков, следует соблюдать осторожность.
При использовании потоков для отображения анимационных последовательно­
стей либо для динамического вывода графики могут возникнуть дополнительные
трудности. Для решения подобных задач применяются различные подходы, однако
наиболее универсальным является использование двойной буферизации. При этом
графические операции выполняются над внеэкранным изображением (картой пиксе­
лей), которая, в свою очередь, выводится на экран.
Параллельная обработка создает дополнительные преимущества при разработке
программ, предназначенных для поддержки сетевого взаимодействия. В следующей
главе будут рассмотрены различные вопросы сетевого обмена, включая разработку
многопотоковых серверов.
СЕТЕВОЕ
ПРОГРАММИРОВАНИЕ

В ЭТОЙ главе...

Реализация универсального сетевого клиента.

Обработка строк с помощью S t r i n g T o k e n i z e r .

Проверка почтового адреса с помощью сетевого клиента.

Получение файлов с HTTP-сервера.

Получение Web-документов с использованием класса URL.

Реализация универсального сервера.

Создание простого HTTP-сервера.

Обеспечение работы сервера в многопотоковом режиме.

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


посредством RMI.
lt~y\3J^3J

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

Диспетчеры защиты большинства броузеров разрешают аплету ус­


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

В этой главе мы расскажем вам об организации непосредственного взаимодействия


между клиентами и серверами с помощью ггсезд (socket). Взаимодействие с помощью
гнезд — один из самых низких уровней обмена по сети. Если вам когда-либо приходилось
работать с гнездами на других языках программирования, вы будете приятно удивлены,
узнав, насколько просто реализуется эта процедура в Java. Класс URL позволяет скрыть
детали сетевого программирования, предоставляя методы для установки соединения и
связывая с гнездами потоки ввода-вывода. Помимо гнезд, с помощью которых можно
организовать обмен с программами, написанными на любом языке, Java предоставляет
два высокоуровневых пакета для взаимодействия с системами конкретных типов — RMI
(Remote Method Invocation — вызов удаленных методов) и JDBC (Java Database Connec­
tivity — Java-взаимодействие с базами данных). Пакет RMI позволяет обращаться к мето-
720 Глава 17. Сетевое программирование

дам удаленных Java-объектов и передавать сериализованные объекты по сети. JDBC дает


возможность передавать SQL-выражения удаленным базам данных. Подробно Java Data­
base Connectivity будет рассматриваться в главе 22.

17.1. Реализация клиента


Сетевые соединения между клиентом и сервером устанавливаются по инициативе
клиентов. Реализация клиента включает следующие шаги.
1. Создание объекта Socket.
2. Создание выходного потока, используемого для передачи информации объекту
Socket.
3. Создание входного потока для чтения ответов сервера.
4. Организация обмена с входным и выходным потоками.
5. Закрытие объекта Socket по окончании сетевого обмена.
Подробно перечисленные действия описаны ниже. Заметьте, что большинство ме­
тодов, описанных в данном разделе, могут генерировать исключение lOException, по­
этому вызовы их должны помещаться в блок t r y / c a t c h .

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


Java-объект Socket реализует гнездо, предназначенное для поддержки сетевого
соединения. Клиент обращается к существующему серверу, который ожидает за­
проса через порт с определенным номером. Как правило, при создании гнезда за­
дается имя узла либо IP-адрес и номер порта. Например:
Socket c l i e n t = new Socket("hostname'\ portNumber);
или
Socket c l i e n t = new Socket("IP a d d r e s s " , portNumber);
Если вы уже занимались программированием сетевого взаимодействия, заметьте,
что, следуя данному подходу, вы создаете гнездо, ориентированное на соединение.
Java также поддерживает гнезда, не использующие соединение (UDP); для этого
применяется класс DatagramSocket.

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


объекту Socket
В языке Java одни и те же методы выполняют запись данных в файл, передачу их
гнезду и вывод в стандартный выходной поток. Потоки данных связываются с со­
ответствующими объектами, реализующими обмен с конкретной физической сре­
дой. Таким образом, все разновидности объекта, используемые для обмена с фай­
лами, пригодны также и для работы с гнездами. В качестве примера можно при­
вести P r i n t W r i t e r . Этот поток позволяет использовать методы p r i n t и p r i n t l n
для записи данных в гнездо точно так же, как они применяются для вывода ин­
формации на экран. Конструктору P r i n t W r i t e r в качестве параметра передается
универсальный объект OutputStream, который можно получить из объекта
Socket, вызывая метод g e t O u t p u t S t r e a m . Передавая конструктору в качестве
1 7 . 1 . Реализация клиента 721

другого параметра значение t r u e , вы можете задать режим автоматического вы­


вода в поток содержимого буфера. В обычных условиях информация остается в
буфере до тех пор, пока он не будет заполнен. Задавая автоматическую передачу
содержимого буфера, вы указываете на то, что необходимо выполнять запись дан­
ных в поток после каждого вызова метода p r i n t I n . П р и м е р использования кон­
структора P r i n t W r i t e r приведен ниже.
P r i n t W r i t e r out =
new P r i n t W r i t e r ( c l i e n t . g e t O u t p u t S t r e a m ( ) , true);
Объект O b j e c t O u t p u t S t r e a m можно применять для передачи по сети Java-
объектов сложной структуры и сборки их на другом конце соединения. Поток
O b j e c t O u t p u t S t r e a m , связанный с сетевым соединением, используется точно
так же, как и аналогичный объект, связанный с файлом: для передачи сериализо-
ванного объекта и всех объектов, на которые он ссылается, используется метод
w r i t e O b j e c t . Сервер, расположенный на другом конце соединения, вызывает
для сборки переданного объекта метод r e a d O b j e c t класса O b j e c t I n p u t S t r e a m .
Заметьте, что все AWT-объекты являются сериализуемыми (допускают запись в
поток), а для того, чтобы сделать сериализуемым любой другой объект, надо лишь
объявить, что он реализует интерфейс S e r i a l i z a b l e . Подробно сериализация
была рассмотрена в главе 13, там же были приведены примеры. Высокоуровневый
интерфейс, использующий сериализацию для передачи распределенных Java-
объектов по сети, будет рассмотрен ниже при обсуждении RMI.

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


После того как вы передали на сервер данные, вы должны прочитать ответ серве­
ра. Как и в случае передачи данных, гнездо не предоставляет никаких специаль­
ных средств для их чтения. Ч т о б ы принимать информацию, переданную серве­
ром, вам надо связать с гнездом стандартный входной поток. Для чтения символь­
ных данных чаще всего применяется объект I n p u t S t r e a m R e a d e r .
InputStreamReader in =
new I n p u t S t r e a m R e a d e r ( c l i e n t . g e t l n p u t s t r e a m ( ) ) ;
Несмотря на то что данный подход реализуется проще других, лучших результатов
можно добиться, помещая стандартный поток I n p u t S t r e a m в состав
Buf f e r e d R e a d e r . П р и этом система осуществляет буферизованный ввод данных
и не обращается к входному потоку при выполнении каждой операции чтения. В
результате за счет незначительного увеличения объема используемой памяти (по
умолчанию размер буфера равен 512 байт) достигается существенное повышение
быстродействия.
BufferedReader in =
new B u f f e r e d R e a d e r
(new I n p u t S t r e a m R e a d e r ( c l i e n t . g e t l n p u t S t r e a m O ) ) ;
Совет

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


использование буферизованного входного потока существенно
увеличит быстродействие программы. Щ^
722 Глава 17. Сетевое программирование

В некоторых случаях программа должна передавать данные серверу, не получая


ответа от него; так, например, работает простой почтовый клиент. В этом случае
обеспечивать описываемый здесь этап сетевого взаимодействия нет необходимо­
сти. Иногда бывает необходимо принимать информацию от сервера, не передавая
ему никаких данных (например, для чтения сведений о текущем времени, распро­
страняемых по сети). В этом случае следует пропустить этап вывода данных в
гнездо и сосредоточиться на их вводе. Однако чаще всего вам придется передавать
и получать информацию по сети. Если сервер реализован на языке Java и передает
по сети объекты сложной структуры, вам надо использовать поток O b j e c t l n p u t -
S t r e a m и вызывать для получения данных метод r e a d O b j e c t .

О р г а н и з а ц и я о б м е н а с в х о д н ы м и в ы х о д н ы м потоками
В состав класса P r i n t S t r e a m входят методы p r i n t n p r i n t l n , которые позволя­
ют передавать по сети переменные простых типов, объекты S t r i n g и строковые
представления других объектов. П р и передаче объекта (ОЬ j e c t ) он преобразуется
в строку путем вызова метода t o S t r i n g соответствующего класса. Эти методы
должны быть знакомы вам, поскольку S y s t e m , o u t , часто используемый в про­
граммах, по сути является экземпляром P r i n t S t r e a m . От класса O u t p u t S t r e a m
P r i n t S t r e a m наследует методы w r i t e , позволяющие передавать отдельные бай­
товые значения или байтовые массивы.
Класс P r i n t W r i t e r похож на P r i n t S t r e a m ; он содержит те же методы p r i n t и
p r i n t I n . Единственное различие между этщяи методами состоит в том, что в
P r i n t W r i t e r можно организовать запись различных наборов символов Unicode.
Класс Buf f e r e d R e a d e r содержит два часто используемых метода: r e a d и r e a d L i n e .
Метод r e a d возвращает единичный символ, а r e a d L i n e читает целую строку и воз­
вращает объект S t r i n g . Оба метода не возвращают управление вызывающей про­
грамме до тех пор, пока данные на будут доступны. Метод r e a d L i n e ожидает появ­
ления символа перевода строки или EOF (этот символ передается тогда, когда сервер
разрывает соединение), поэтому применять его надо тогда, когда вы уверены, что по
окончании передачи сервер закроет гнездо, либо тогда, когда вы знаете число строк,
которые должен передать сервер. П р и получении EOF метод r e a d L i n e возвращает
значение n u l l .

З а к р ы т и е о б ъ е к т а Socket п о о к о н ч а н и и с е т е в о г о о б м е н а
Когда обмен данными по сети закончен, надо закрыть гнездо с помощью метода c l o s e .
client.close();
Этот метод также закрывает входной и выходной потоки, связанные с гнездом.

Пример универсального сетевого клиента


Код, приведенный в листинге 17.1, иллюстрирует описанный выше подход. Работа
начинается с вызова метода c o n n e c t , он инициирует соединение, а затем передает
управление методу h a n d l e C o n n e c t i o n , который выполняет реальный обмен. Пред­
ставленный вариант метода h a n d l e C o n n e c t i o n сообщает об установке соединения,
передает на сервер одну строку ( " G e n e r i c N e t w o r k C l i e n t " ) , читает и выводит
17.1. Реализация клиента 723

строку ответа и завершает работу. П р и разработке реального клиента на базе этой


программы надо переопределить handle Connect ion, реализовав в нем требуемые
действия. Метод connect можно оставить без изменений.

Листинг 17.1.NetworkClient.Java

import java.net.*;
import java.io.*;

/** Программа, реализующая простой сетевой клиент и


* допускающая модификацию. Для того чтобы обеспечить
* выполнение требуемых действий, надо изменить код метода,
* однако код метода connect чаще всего остается без изменений.
* Для того чтобы упросить создание PrintWriter и
* BufferedReader, используется SocketUtil.

public class NetworkClient {


protected String host;
protected int port;

/•• Регистрация адреса узла и номера порта. Для


* установления соединения надо вызывать метод
* connect.
V
public NetworkClient(String host, int port) {
this.host = host;
this.port = port;
}
/•* Метод connect устанавливает соединение, a затем
* вызывает метод handleConnection.
V
public void connect() {
try {
Socket client = new Socket(host, port);
handleConnection(client);
} catch(UnknownHostException uhe) {
System.out.println("Unknown host: " + host);
uhe.printStackTrace() ;
} catch(lOException ioe) {
System.out.println("lOException: " + ioe);
ioe.printStackTrace();
}
}

/** При создании реального клиента надо переопределить


* метод handleConnection. Версия данного метода
* по умолчанию передает на сервер строку символов
* ("Generic Network Client"), читает одну строку ответа
* сервера, выводит ее на печать и завершает свою работу.
V
724 Глава 17. Сетевое программирование

protected void handleConnection(Socket client)


throws lOException {
PrintWriter out = SocketUtil.getWriter(client);
BufferedReader in = SocketUtil.getReader(client);
out.println("Generic Network Client");
System.out.println
("Generic Network Client:\n" +
"Made connection to " + host +
" and got '" + in.readLine() + "• in response");
client.close 0 ;
}

/** Имя сервера, с которым установлено соединение. */

public String getHostO {


return(host);
}
/** Номер порта, по которому установлено соединение. */

public int getPortO {


return(port);
}

Класс SocketUtil (листинг 17.2) представляет собой лишь постой интерфейс для
вызова конструкторов Buf feredReader и PrintWriter.

Листинг 17.2.SocketUtil.Java

import java.net.*;
import java.io.*;

/** Средства для создания BufferedReader и PrintWriter,


* связанных с объектом Socket.

public class SocketUtil {


/** Создание BufferedReader для получения входных данных. */

public static BufferedReader getReader(Socket s)


throws lOException {
return(new BufferedReader(
new InputStreamReader(s.getlnputStreamO)));
}
/** Создание PrintWriter для передачи выходных данных.
* При выполнении метода println объект PrintWriter
* автоматически передает данные, содержащиеся в буфере.
V
public static PrintWriter getWriter(Socket s)
throws lOException {
17.2. Разбор строк с помощью класса StringTokenizer 725

// Значение true второго параметра задает автоматическую


// передачу содержимого буфера.
return(new PrintWriter(s.getOutputStream(), true));
}
}

И, наконец, класс NetworkClientTest, код которого показан в листинге 17.3, по­


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

Листинг 17.3. NetworkClientTest.Java

/** Соединение с заданным узлом с использованием указанного


номера порта.
V

public class NetworkClientTest {


public static void main(String[] args) {
String host = "localhost";
int port = 8088;
if (args.length > 0) {
host = args[0];
}
if (args.length > 1) {
port = Integer.parseint(args[1]);
}
NetworkClient nwClient = new NetworkClient(host, port);
nwClient.connect();
}

Соединение с FTP-сервером
Воспользуемся программой, код которой приведен в листинге 17.3, для соедине­
ния с общедоступным FTP-сервером Netscape, который принимает обращения через
порт 21. В данном примере символ ">" представляет собой приглашение для ввода
строки в системе DOS или UNIX.
> Java N e t w o r k C l i e n t T e s t f t p . n e t s c a p e . c o m 21
G e n e r i c Network C l i e n t :
Made c o n n e c t i o n t o f t p . n e t s c a p e . c o m and g o t ' 2 2 0 f t p 2 6 FTP s e r v e r
(UNIX(r) System V R e l e a s e 4 . 0 ) r e a d y . * i n r e s p o n s e

17.2. Разбор строк с помощью класса


StringTokenizer
При разработке сетевых программ часто возникает задача разбора строк и выде­
ления отдельных компонентов. Приложив определенные усилия, это можно сделать с
помощью методов класса S t r i n g . Однако Java-платформа предоставляет встроенный
класс S t r i n g T o k e n i z e r , позволяющий упростить решение данной задачи. Этот
726 Глава 17. Сетевое программирование

класс не относится к сетевым средствам (он расположен не в составе j a v a . n e t , а в


пакете j a v a . u t i l ) , однако, поскольку обработка строк играет важную роль в напи­
сании сетевых программ клиент-сервер, мы рассматриваем класс S t r i n g T o k e n i z e r в
данной главе.

Класс StringTokenizer
Объект, предназначенный ддя разбора текста, создается на базе конкретной стро­
ки, после чего для получения лексем используется метод n e x t T o k e n . Набор ограни­
чителей, используемый для разделения лексем, задается либо при создании объекта,
либо передается в качестве параметра методу n e x t T o k e n . Данный класс также позво­
ляет узнать конкретное число оставшихся лексем ( c o u n t T o k e n s ) либо определить,
осталась ли в составе объекта хотя бы одна лексема ( h a s M o r e T o k e n s ) . Наиболее час­
то используемые методы описаны ниже.

Конструкторы
p u b l i c StringTokenizer(String input)
Данный конструктор создает объект разбора указанной строки, используя в каче­
стве разделителей пробелы, а также символы табуляции, новой строки и возврата
каретки. В состав возвращаемой лексемы разделители не входят.

p u b l i c StringTokenizer(String input, String delimiters)


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

public StringTokenizer(String input, String d e l i m i t e r s , b o o l e a n


includeDelimiters)
Указанный конструктор строит объект разбора строки, используя заданный набор
разделителей. Если значение третьего параметра равно t r u e , разделители вклю­
чаются в состав возвращаемых лексем.

1\/1етоды
public String nextToken()
Данный метод возвращает следующую лексему. Если в строке не осталось ни одно­
го символа кроме разделителя, n e x t T o k e n генерирует исключение N o S u c h -
ElementException.

public String n e x t T o k e n ( S t r i n g delimiters)


Указанный метод изменяет набор ограничителей, а затем возвращает следующую
лексему. Как и предыдущий вариант n e x t T o k e n , если в строке не осталось ни од­
ного символа кроме разделителей, данный метод генерирует исключение NoSuch-
ElementException.
17.2. Разбор строк с помощью класса StringTokenizer 727

public int c o u n t T o k e n s ( )
Метод c o u n t T o k e n s возвращает число лексем, оставшихся в строке. Определение
количества лексем осуществляется исходя из текущего набора разделителей.

public b o o l e a n h a s M o r e T o k e n s ( )
Метод h a s M o r e T o k e n s позволяет определить, осталась ли в строке хотя бы одна
лексема; при этом используется текущий набор разделителей. Большинство при­
ложений перед вызовом метода n e x t T o k e n определяют, осталась ли в строке хотя
бы одна лексема, либо помещают вызов n e x t T o k e n в блок t r y / c a t c h , в котором
обрабатывается исключение N o S u c h E l e m e n t E x c e p t i o n , Заметьте, что при вызо­
ве h a s M o r e T o k e n s могут возникать побочные эффекты, поскольку этот метод
влияет на значение внутреннего счетчика. Так, если вы вызовете h a s M o r e T o k e n s
с одним набором разделителей, а затем n e x t T o k e n с другим набором, результаты
могут быть непредсказуемыми.

Пример интерактивной программы разбора


в качестве примера использования StringTokenizer рассмотрим программу, код
которой представлен в листинге 17.4. Эта программа позволяет вводить строку и раз­
делители и возвращает набор лексем.

Листинг 17.4.TokTest.Java

import java.util.StringTokenizer;

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


* а второй параметр - как набор разделителей.
V

public class TokTest {


public static void main(String[] args) {
if (args.length == 2) {
String input = args[0], delimiters = args[l];
StringTokenizer tok =
new StringTokenizer(input, delimiters);
while (tok.hasMoreTokens0) {
System.out.println(tok.nextToken());
}
} else {
System.out.println
("Usage: Java TokTest string delimeters");

Результаты выполнения TokTest.


> Java TokTest http://www. microsoft .com/'»'gates/ :/.
http
728 Глава 17. Сетевое программирование

microsoft
com
-gates
> Java TokTest "if (tok.hasMoreTokens()) {" " (){ . "
if
tok
hasMoreTokens

17.3. Пример клиента, предназначенного


для проверки почтовых адресов
Наилучший способ изучить на практике особенности работы конкретного прото­
кола — открыть соединение с сервером через указанный порт и попытаться вручную
организовать обмен с ним. Для этого можно использовать клиент-программу t e l n e t .
Предположим, что вам надо непосредственно соединиться с почтовым сервером и
проверить, существует ли конкретный почтовый адрес. Для того чтобы организовать
взаимодействие с почтовым сервером, необходимо знать, что он использует при ра­
боте протокол SMTP (Simple Mail Transfer Protocol — простой протокол передачи
почты), после установления соединения передает клиенту одну или несколько строк
символов, а затем переходит в режим ожидания команды. Для получения информа­
ции о пользовательских именах используется команда е х р п имя_пользователя, а для
завершения соединения — команда q u i t . П р и интерпретации команд SMTP-сервер не
учитывает регистр символов. В листинге 17.5 приведен пример сеанса взаимодейст­
вия с почтовым сервером, расположенным по адресу а р 1 . j hu . edu.

Листинг 17.5. Взаимодействие с почтовым сервером

> t e l n e t a p l . j h u . e d u 25
Trying 1 2 8 . 2 2 0 . 1 0 1 . 1 0 0 . . .
Connected t o a p l c e n M P . a p l . j h u . e d u .
Escape c h a r a c t e r i s ' ' ^ j ' .
220 a p l c e n M P . a p l . j h u . e d u ESMTP S e n d m a i l 8 . 9 . 3 / 8 . 9 . 1 ; S a t , 10 Feb 2001
12:05:42
500 (EST)
expn h a l l
250 Marty Hall <hall(?aplcenMP. apl. jhu. edu>
expn root
250-Tom Vellani <vellani@aplcenMP.apl.jhu.edu>
250 Gary Gafke <gary@aplcenMP.apl.jhu.edu>
quit
221 aplcenMP.apl.jhu.edu closing connection
Connection closed by foreign host.

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


вых адресов, должна выполнять разбор адреса, выделяя в нем имя пользователя и до­
менное имя узла, передавать команду е х р п , читать и выводить результаты, а затем пе­
редавать серверу команду q u i t . Ответ SMTP-сервера может содержать несколько
строк. Как вы знаете, если соединение остается открытым, метод r e a d L i n e не воз-
17.3. Пример клиента, предназначенного... 729

вращает управление до тех пор, пока на будет получен символ перевода строки. По­
этому данный метод нельзя использовать при создании клиента, предназначенного
для проверки адресов. Вместо метода readLine мы должны использовать метод
read, передавая ему в качестве параметра ссылку на байтовый массив, достаточно
большой для того, чтобы вместить ответ сервера, а затем с помощью метода w r i t e
вывести содержимое массива. Код, реализующий указанные действия, приведен в
листинге 17.6, а в листинге 17.7 представлен код вспомогательного класса, предна­
значенного для разбора почтового адреса. В частности, этот класс выделяет имя
пользователя и адрес узла.

Листинг 17.6. AddressVerif i e r . java

import j a v a . n e t . * ;
import j a v a . i o . * ;

/** Почтовый адрес задается в формате пользователь@узел.


* Соединение устанавливается через порт 25 и на сервер
* передается запрос *ехрп'. Результаты выводятся на печать.
V
public class AddressVerifier extends NetworkClient {
p r i v a t e String username;

public s t a t i c void main(String[] args) {


if (args.length != 1) {
usage();
}
MailAddress address = new MailAddress(args[0]);
AddressVerifier v e r i f i e r
= new AddressVerifier(address.getUsername0 ,
address.getHostname0, 25);
verifier.connect();
}

public AddressVerifier(String username, String hostname,


i n t port) {
super(hostname, p o r t ) ;
this.username = username;

/ • • Родительский класс NetworkClient автоматически


* устанавливает соединение и вызывает метод
* handleConnection. В теле этого метода выполняются
* все действия, необходимые для обмена с сервером.

// Метод readLine нельзя использовать, поскольку он


/ / блокирует выполнение программы. Блокирование допустимо
/ / только в тех случаях, когда вы знаете число строк,
/ / которые должны быть прочитаны. Почтовый сервер передает
/ / различное число строк либо закрывает соединение.
/ / В данном случае считается, что размер буфера 1000 байт
/ / достаточен для хранения начального сообщения сервера,
/ / а также для хранения ответа на запрос EXPN.
730 Глава 17. Сетевое программирование

protected void handleConnection(Socket client) {


try {
PrintWriter out = SocketUtil.getWriter(client);
InputStream in = client.getInputStream();
byte[] response = new byte[1000];
// Начальное сообщение сервера.
in.read(response);
out.println("EXPN " + username);
// Ответ на команду EXPN.
int numBytes = in.read(response);
// Значение 0 указывает на то, что применяется код ASCII.
System.out.write(response, 0, numBytes);
out.println("QUIT");
client.close();
} catch(lOException ioe) {
System.out.println("Couldn't make connection: " + ioe);

/** Сообщение о том, что параметры заданы некорректно. ^

public static void usage() {


System.out.println ("You must supply an email address
"of the form *username@hostname'.") ;
System.exit(-1);
}

Листинг 17.7. MailAddress. Java

import j a v a . u t i l . * ;
/** Получение строки в формате "пользователь@узел" и
* разделение ее на компоненты, соответствующие
* пользователю и узлу.
*/

public class MailAddress {


private String username, hostname;

public MailAddress(String emailAddress) {


StringTokenizer tokenizer
= new StringTokenizer(emailAddress, " @ " ) ;
this.username = getArg(tokenizer);
this.hostname = getArg(tokenizer);
}
private static String getArg(StringTokenizer tok) {
try { return(tok.nextToken0); }
catch (NoSuchElementException nsee) {
System.out.println("Illegal email address");
System.exit(-1);
return(null) ;
}
17.4. Пример клиент-программы... 731

public String getUsername() {


return(username);
}
public String getHostname() {
return(hostname);

Ниже показан пример использования данной клиент-программы.


> Java AddressVerifier tbl@w3.org
250 <timbl@hq.lcs.mit.edu>
> Java AddressVerifier tiinbl@hq.lcs.mit.edu
250 Tim Berners-Lee <timbl>
> Java AddressVerifier gosling@mail.javasoft.com
550 gosling... User unknown

17.4. Пример клиент-программы,


предназначенной для получения
ресурса по указанному URL
Получить документ посредством протокола HTTP достаточно просто. Для этого
надо установить соединение с узлом, на котором расположен документ (при установ­
лении соединения указывается порт, с которым связан протокол H T T P ) , передать
команду GET, за которым после пробела следует адрес документа и последователь­
ность символов " Н Т Т Р / 1 . О", которая также отделена от адреса документа пробелом.
Ответ сервера можно читать построчно. В отличие от программы, представленной в
листинге 17.3, в данном случае можно использовать метод r e a d L i n e , поскольку по
окончании передачи информации сервер закрывает соединение и метод r e a d L i n e
возвращает значение n u l l .
Данный подход достаточно прост, но даже в этом случае разработчику приходится
выполнять работу сверх необходимого минимума. Дело в том, что Java содержит
встроенные классы URL и U R L C o n n e c t i o n , которые еще больше упрощают процесс
взаимодействия с HTTP-серверами. Эти классы будут рассмотрены далее в этой главе,
а сейчас мы "вручную" реализуем соединение с сервером; это поможет вам лучше по­
нять особенности протокола HTTP и научиться работать с теми протоколами, для ко­
торых не реализованы вспомогательные методы. Пример telnet-соединения с HTTP-
сервером, расположенным по адресу www. c o r e w e b p r o g r a m m i n g . com, показан в лис­
тинге 17.8. Установка соединения выполняется через порт 80.

Листинг 17.8. Получение HTML-документа через telnet-соединение

Unix> t e l n e t www.corewebprograimning.com 80
Trying 2 1 6 . 2 4 8 . 1 9 7 . 1 1 2 . . .
Connected t o www.corewebprograinming.com.
Es:?.pe character is ' ^ ] ' .
732 Глава 17. Сетевое программирование

GET / НТТР/1.0

НТТР/1.1 200 ОК
Date: Sat, 10 Feb 2001 18:04:17 GMT
Server: Apache/1.3.3 (Unix) PHP/3.0.11 FrontPage/4.0.4.3
Connection: close
Content-Type: text/html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>

</HTML>
C o n n e c t i o n c l o s e d by f o r e i g n host.

В данном примере для получения документа используется запрос GET. Возможно, в


некоторых случаях вы захотите ограничиться получением HTTP-заголовка для кон­
кретного документа. Так, например, программа, которая проверяет корректность ги­
пертекстовых ссылок не обязательно должна получать текст документа. Чтобы убедить­
ся, что Web-страница, на которую указывает ссылка, реально существует, достаточно
получить заголовок документа. Поэтому, используя в программе проверки ссылок за­
прос HEAD вместо запроса GET, вы можете существенно снизить нагрузку на клиент-
машину и на серверы. В Java не предусмотрены вспомогательные классы для работы с
запросом HEAD, однако, чтобы реализовать запросы данного типа, достаточно внести
незначительные изменения в код программы, которая будет рассмотрена ниже.

Класс, предназначенный для получения ресурса


с указанного узла
в листинге 17.9 приведен код класса, который получает файл по заданному имени
узла, номеру порта и URI (части URL, соответствующей пути к ресурсу). Указанные
сведения передаются в виде отдельных параметров. Для передачи строки, содержа­
щей запрос GET, приложение использует класс N e t w o r k C l i e n t , который был приве­
ден в листинге 17.1.Значения, полученные от сервера, читаются построчно и выво­
дятся в стандартный выходной поток.

Листинг 1 7 . 9 . U r i R e t r i e v e r , J a v a

import java.net.*;
import java.io.*;

/ * * В командной с т р о к е з а д а ю т с я параметры, определяющие


* у з е л , порт и файл.
V
p u b l i c c l a s s U r i R e t r i e v e r extends NetworkClient {
private String u r i ;
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
UriRetriever uriClient
17.4. Пример клиент-программы... 733

= new UriRetriever(args[0], Integer.parseint(args[1]) ,


args[2]);
uriClient.connect() ;
}
public UriRetriever(String host, int port, String uri) {
super(host, port);
this.uri = uri;
}
/** Передается строка GET, затем результаты читаются
* построчно и выводятся в стандартный выходной поток.
V
// В данном случае может использоваться метод readLine.
// Блокирование выполнения программы допустимо, поскольку
// HTTP-сервер закрывает соединение, в результате чего
// метод readLine возвращает значение null.
protected void handleConnection(Socket uriSocket)
throws lOException {
PrintWriter out = SocketUtil.getWriter(uriSocket);
BufferedReader in = SocketUtil.getReader(uriSocket);
out.printlnC'GET " + uri + " HTTP/1. 0\n") ;
String line;
while ((line = in.readLine()) ?= null) {
System.out.printin("> " + line);
}
}

Класс, предназначенный для получения ресурса


по заданному URL
Работая с программой, рассмотренной выше, пользователь должен отдельно зада­
вать имя узла, порт и URI. В листинге 17.10 показан код программы, которая произ­
водит разбор URL с помощью объекта S t r i n g T o k e n i z e r , а затем передает компо­
ненты URL U r i R e t r i e v e r .

Листинг 1 7 . 1 0 . U r i R e t r i e v e r . J a v a

import java.util.*;

/** Разбор выходной строки и выделение компонентов,


* соответствующих узлу, порту и файлу. Полученные значения
* передаются объекту UriRetriever, который получает
* указанный русурс.
V
public class UriRetriever {
public static void main(String[] args) {
checkUsage(args);
StringTokenizer tok = new StringTokenizer(args[0]);
734 Глава 17. Сетевое программирование

string protocol = tok.nextToken(":");


checkProtocol(protocol);
String host = tok.nextToken(":/");
String uri;
int port = 80;
try {
uri = tok.nextToken("");
if (uri.charAt(0) == ':') {
tok = new StringTokenizer(uri);
port = Integer.parseint(tok.nextToken(":/"));
uri = tok.nextToken("");
}
} catch(NoSuchElementException nsee) {
uri = "/";
}
UriRetriever uriClient = new UriRetriever(host, port, uri);
uriClient.connect();
}

/** Сообщение о том, что URL не задан. */

private static void checkUsage(String[] args) {


if (args.length != 1) {
System.out.println("Usage: UriRetriever <URL>");
System.exit(-1) ;
}
}

/** Сообщение о том, что поддерживается только протокол HTTP. */

private static void checkProtocol(String protocol) {


if (!protocol.equals("http")) {
System.out.println("Don't understand protocol " + protocol);
System.exit(-1);
}

Выходные данные UriRetriever


Используется порт по умолчанию:
Prompt> Java UriRetriever
http://www.microsoft.com/netscape-beats~ie.html
> HTTP/1.1 404 Object Not Found
> Server: Microsoft-IIS/5.0
> Date: Fri, 31 Mar 2000 18:22:11 GMT
> Content-Length: 3243
> Content-Type: text/html
>
> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
> <html dir=ltr>Explicit port number:
17.5, Класс URL 735

Н о м е р порта задан явно:


Prompt> Java UrlRetriever
http://home.netscape.com:80/ie-beats-netscape.html
> HTTP/1.1 404 Not found
> Server: Netscape-Enterprise/3.6
> Date: Fri, 04 Feb 2000 21:52:29 GMT
> Content-type: text/html
> Connection: close
>
> <TITLE>Not Found</TITLE><Hl>Not Found</Hl> The r e q u e s t e d o b j e c t
d o e s n o t e x i s t on t h i s s e r v e r . The l i n k you f o l l o w e d i s e i t h e r o u t ­
d a t e d , i n a c c u r a t e , o r t h e s e r v e r h a s b e e n i n s t r u c t e d n o t t o l e t you
have i t .

Как видите, мы создали броузер. Осталось "совсем немного" — обеспечить форма­


тирование полученных результатов. Однако то, что мы имеем, — уже немало, учиты­
вая размеры кода. В следующем разделе мы сократим объем кода приблизительно
вдвое за счет использования класса URL. Далее в этой главе мы реализуем простой
пользовательский интерфейс, позволяющий формировать HTTP-запросы в интерак­
тивном режиме и просматривать результаты. Вспомните также, что в главе 14 мы рас­
сказали вам, как использовать компонент J E d i t o r Рапе для форматирования HTML-
кода и обеспечения перехода по ссылкам.

1 7 . 5 . К л а с с URL
Класс URL упрощает доступ к документам, расположенным на Web-серверах. Ме­
тоды данного класса обеспечивают разбор строки, содержащей URL, позволяя извле­
кать имя протокола (например, h t t p ) , имя узла (например, j a v a . s u n . c o m ) , номер
порта (например, 80) и имя файла (например, / r e p o r t s / e a r n i n g s . h t m l ) . Класс
URL также предоставляет простой интерфейс для чтения удаленных файлов.

Получение ресурса по указанному URL


Несмотря на то что написание клиент-программы, предназначенной для соедине­
ния с HTTP-сервером и получения указанного документа, не требует затраты больших
усилий, данная задача решается настолько часто, что разработчики Java реализовали
специальный вспомогательный класс J a v a . n e t . URL. С этим классом вы уже встреча­
лись в разделе 9.5: объект URL передается при вызове метода д е t A p p l e t -
C o n t e x t () . showDocument. Кроме того, объект URL может быть использован для
разбора строки и чтения содержимого документа. П р и м е р использования объекта
URL приведен в листинге 17.1L

Листинг 1 7 . 1 1 . U r l R e t r i e v e r 2 . J a v a

import java.net.*;
import java.io.*;
/** Получение файла с у д а л е н н о г о у з л а п о с р е д с т в о м к л а с с а URL.
736 Глава 17. Сетевое программирование

public class UrlRetriever2 {


public static void main(String[] args) {
checkUsage(args) ;
try {
URL url = new URL(args[0]);
BufferedReader in = new BufferedReader(
new Input St reamReader (url .openStreamO ) ) ;
String line;
while ((line = in.readLine()) ?= null) {
System.out.printin("> " + line);
}
in.close();
} catch(MalformedURLException mue) { // Конструктор URL
System.out.println(args[0] + "is an invalid URL: " +
} catch(lOException ice) { // Конструкторы потоков
System.out.println("lOException: " + ioe);
}
}
private static void checkUsage(String[] args) {
if (args.length != 1) {
System.out.println("Usage: UrlRetriever2 <URL>");
System.exit(-1);
}
}

Н и ж е показаны результаты работы UrlRetriever2.


Prompt> Java UrlRetriever2 http://www.whitehouse.gov/
> <HTML>
> <HEAD>
> <TITLE>Welcome To The White House</TITLE>
> </HEAD>
> ... Остальная часть HTML-документа ...
> </HTML>

Как видите, данная программа выводит содержимое документа, а не HTTP-ответ


сервера, как это было при использовании класса U r l R e t r i e v e r . При необходимости
эту информацию может предоставить другой Java-класс URLConnection. Для созда­
ния экземпляра класса URLConnection надо вызвать метод openConnection сущест­
вующего объекта URL. Сделав это, вы можете использовать различные методы для по­
лучения информации, содержащейся в заголовке HTTP-ответа (например,
getContentType и getLastModif ied). Дополнительную информацию о данном
классе вы найдете в разделе j a v a . n e t . URLConnection документации на API.

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


Основными методами являются конструкторы класса, а также методы, предназна­
ченные для разбора строкового представления URL и связывания входного потока с
объектом URL. Как вы увидите из последующих разделов, класс URL может приме­
няться и в других целях.
17.5. Класс URL 737

public URL(String absoluteSpec)


public U R L ( U R L base, String relativeSpec)
public URL(String p r o t o c o l , String h o s t . String file)
public URL(String p r o t o c o l . String h o s t , int port. String file)
Указанные четыре конструктора представляют различные способы построения объ­
ектов URL. Все они могут генерировать исключение Malf ormedURLException.

public String getFileO


Данный метод возвращает часть URL, представляющую имя файла (см. листинг 17.12).

public String getHostO


Метод g e t H o s t возвращает часть URL, определяющую имя узла (см. листинг 17.12).

public int getPortO


Если порт был указан явно, метод g e t P o r t возвращает заданный номер порта. В
противном случае возвращается значение -1 (не 80) (см. листинг 17.12).

public String getProtocol()


Метод g e t P r o t o c o l возвращает часть URL, определяющую используемый прото­
кол (см. листинг 17.12).

public String getRef()


Метод g e t R e f возвращает часть URL, определяющую ссылку внутри документа
(см. листинг 17.12).

public final InputStream openStream()


Метод o p e n s t г earn возвращает входной поток, который используется для чтения
информации, передаваемой сервером. Данный метод может генерировать исклю­
чение l O E x c e p t i o n .

public U R L C o n n e c t i o n o p e n C o n n e c t i o n ( )
Данный метод возвращает объект U R L C o n n e c t i o n , который может использовать­
ся для получения информации, передаваемой в заголовке ответа и для передачи
данных HTTP-серверу (запрос POST). Подробно запрос POST будет обсуждаться в
главе 19.

public String toExternalForm()


Метод t o E x t e r n a l F o r m позволяет получить строковое представление URL. Дан­
ный метод выполняет те же действия, что и метод t o S t r i n g . П р и м е р использова­
ния некоторых из описанных методов приведен в листинге 17.12.
738 Глава 17. Сетевое программирование

Листинг 1 7 . 1 2 . U r l T e s t . J a v a

import java.net.*;

/•• Чтение URL, заданного в командной строке, и вывод


* его компонентов.

public class UrlTest {


public static void main(String[] args) {
if (args.length == 1) {
try {
URL url = new URL(args [0]);
System.out.println
URL: " + url.toExternalFormO + "\n" +
File: + url.getFileO + "\n" +
Host: + url.getHost0 + "\n" +
Port: + url.getPort0 + "\n" +
Protocol: + url.getProtocol0 + "\n"
Reference: + url.getRef0);
} catch(MalformedURLException mue) {
System.out.println("Bad URL.");
}
else
System.out.printlnC'Usage: UrlTest <URL>")

Н и ж е показаны результаты выполнения UrlTest.


> Java UrlTest http://www.irs.g0v/mission/#scjueeze-them-dry
URL: http://www.irs.gov/mission/#squeeze-them-dry
File: /mission/
Host: www.irs.gov
Port: -1
Protocol: http
Reference: squeeze-them-dry

17.6. WebClient: интерактивное


взаимодействие с Web-сервером
В предыдущих разделах мы рассматривали программы, которые получали URL из
командной строки и выводили содержимое документа в стандартный выходной по­
ток. В данном разделе описывается класс W e b C l i e n t , который представляет собой
простой графический интерфейс к HTTP-серверу. Программа, код которой показан в
листинге 17.13, принимает от пользователя основные компоненты заголовка HTTP-
запроса. После щелчка на кнопке Submit Request запрос передается серверу; заголо­
вок запроса завершается пустой строкой. Ответ сервера отображается в составе ком­
понента J T e x t A r e a , допускающего возможность прокрутки. Копирование документа
осуществляется в отдельном потоке, поэтому пользователь всегда может прервать
этот процесс.
17.6.WebCllent... 739

Сетевое взаимодействие поддерживает класс H t t p C l i e n t , код которого пред­


ставлен в листинге 17.14. Он передает строку запроса и поля заголовка Web-серверу, а
затем построчно читает ответ сервера и помещает его в состав компонента
JTextArea. Эта процедура продолжается до тех пор, пока сервер не закроет соеди­
нение либо пока работа класса H t t p C l i e n t не будет прервана посредством флага
islnterrupted.
lOiacc L a b e l e d T e x t F i e l d , приведенный в листинге 17.15, представляет собой со­
четание объектов J T e x t F i e l d и J L a b e l . Эта конструкция располагается в верхней
панели WebClient и предназначена для ввода данных пользователем. Интерфейс
I n t e r r u p t i b l e , код которого показан в листинге 17.16, предназначен для иденти­
фикации классов, содержащих метод i s l n t e r r u p t e d . Используя I n t e r r u p t i b l e ,
H t t p C l i e n t опрашивает WebClient с целью определить, прервал ли пользователь
выполнение программы.
Исходный код данной программы находится по адресу h t t p : / / w w w . c o r e w e b -
programming. com/ и может быть использован без ограничений.
Процесс взаимодействия с сервером, находящимся по адресу w w w . c o r e s e r v l e t s .
com, показан на рис. 17.1. В данном случае серверу передается только запрос GET; поля
заголовка не используются. Подробно поля заголовка, включаемые в состав HTTP-
запроса клиента и HTTP-ответа сервера, будут рассмотрены в главе 19.

Листинг 17.13. WebCHent.java

import j a v a . a w t . * ; / / Для BorderLayout, GridLayout, Font, Color,


import J a v a . a w t . e v e n t . * ;
import j a v a . u t i l . * ;
import j a v a x . s w i n g . * ;
/** Клиент-программа с графическим интерфейсом, которая позволяет
* устанавливать соединение с Web-серверами и формировать
* запросы.
V
public class WebClient extends JPanel
implements Runnable, Interruptible, ActionListener {
public static void main(String[] args) {
WindowUtilities.setNativeLookAndFeel();
WindowUtilities.openlnJFrame(new WebClient(), 600, 700,
"Web Client",
SystemColor.control);

private LabeledTextField hostField, portField,


requestLineField;
private JTextArea requestHeadersArea, resultArea;
private String host, requestLine;
private int port;
private String[] requestHeaders = new String[30];
private JButton submitButton, interruptButton;
private boolean islnterrupted = false;

public WebClient0 {
setLayout(new BorderLayout(5, 30));
740 Глава 17. Сетевое программирование

int fontSize = 1 4 ;
Font labelFont =
new Font("Serif", Font.BOLD, fontSize);
Font headingFont =
new Font("SansSerif", Font.BOLD, fontSize+4);
Font textFont =
new Font("Monospaced", Font.BOLD, fontSize-2);
JPanel inputPanel = new JPanelO;
inputPanel.setLayout(new BorderLayout());
JPanel labelPanel = new JPanelO;
labelPanel.setLayout(new GridLayout(4,1));
hostField = new LabeledTextField("Host:", labelFont,
30, textFont);
portField = new LabeledTextField("Port:", labelFont,
"80", 5, textFont);
// Для совместимости с большинством серверов используется
// протокол HTTP 1.0. Применяя средства HTTP 1.1,
// необходимо включить в состав запроса
// поле заголовка Host.

requestLineField =
new LabeledTextField("Request Line:", labelFont,
"GET / HTTP/1.0", 50, textFont);
labelPanel.add(hostField);
labelPanel.add(portField);
labelPanel.add(requestLineField);
JLabel requestHeadersLabel =
new JLabel("Request Headers:");
requestHeadersLabel,setFont(labelFont);
labelPanel.add(requestHeadersLabel);
inputPanel.add(labelPanel, BorderLayout.NORTH);
requestHeadersArea = new JTextArea(5, 80);
requestHeadersArea.setFont(textFont);
JScrollPane headerScrollArea =
new JScrollPane(requestHeadersArea);
inputPanel.add(headerScrollArea, BorderLayout.CENTER);
JPanel buttonPanel = new JPanelO;
submitButton = new JButton("Submit Request");
submitButton.addActionListener(this);
submitButton.setFont(labelFont);
buttonPanel.add(submitButton);
inputPanel.add(buttonPanel, BorderLayout.SOUTH);
add(inputPanel, BorderLayout.NORTH);
JPanel resultPanel = new JPanelO;
resultPanel.setLayout(new BorderLayout());
JLabel resultLabel =
new JLabel("Results", JLabel.CENTER);
resultLabel.setFont(headingFont);
resultPanel.add(resultLabel, BorderLayout.NORTH);
resultArea = new JTextArea();
resultArea.setFont(textFont);
JScrollPane resultScrollArea =
new JScrollPane(resultArea);
resultPanel.add(resultScrollArea, BorderLayout.CENTER);
JPanel interruptPanel = new JPanelO;
interruptButton = new JButton("Interrupt Download");
17.6.WebClJent... 741

interruptButton.addActionListener(this);
interruptButton.setFont(labelFont);
interruptPanel.add(interruptButton);
resultPanel.add(interruptPanel, BorderLayout.SOUTH);
add(resultPanel, BorderLayout.CENTER);
}

public void actionPerformed(ActionEvent event) {


if (event.getSource() == submitButton) {
Thread downloader = new Thread(this);
downloader.start() ;
} else if (event.getSource() == interruptButton) {
islnterrupted = true/
}
}

public void run() {


islnterrupted = false;
if (hasLegalArgs())
new HttpClient(host, port, requestLine,
requestHeaders, resultArea, this);
}

public boolean islnterrupted() {


return(islnterrupted);
}
private boolean hasLegalArgs() {
host = hostField.getTextFieldO.getText0;
if (host.length 0 == 0) {
report("Missing hostname");
return(false);
}
String portString =
portField.getTextField0 .getText();
if (portString.length 0 == 0) {
report("Missing port number");
return(false);
}
try {
port = Integer.parseint(portString);
} catch(NumberFormatException nfe) {
report("Illegal port number: " + portString);
return(false);
}
requestLine =
requestLineField.getTextField().getText();
if (requestLine.length() == 0) {
report("Missing request line");
return(false);
}
getRequestHeaders();
return(true);

private void report(String s)


742 Глава 17. Сетевое программирование

resultArea.setText(s);
}
private void getRequestHeaders() {
for(int i=0; i<requestHeaders.length; i++) {
requestHeaders[i] = null;
}
int headerNum = 0;
String header =
requestHeadersArea.getText();
StringTokenizer tok =
new StringTokenizer(header, "\r\n");
while (tok.hasMoreTokens()) {
requestHeaders[headerNum++] = tok.nextToken();
}
}

Листинг 17.14.HttpClient.Java

import java.net.*;
import java.io.*;
import javax.swing.*;
/•• Сетевой клиент, используемый классом WebClient. */
public class HttpClient extends NetworkClient {
private String requestLine;
private String[] requestHeaders;
private JTextArea outputArea;
private Interruptible app;

public HttpClient(String host, int port.


String requestLine, String[] requestHeaders,
JTextArea outputArea, Interruptible app) {
super(host, port);
this.requestLine = requestLine;
this.requestHeaders = requestHeaders;
this.outputArea = outputArea;
this.app = app;

if (checkHost(host)) {
connect() ;
}
}
protected void handleConnection(Socket uriSocket)
throws lOException {
try {
PrintWriter out = SocketUtil.getWriter(uriSocket);
BufferedReader in = SocketUtil.getReader(uriSocket);
outputArea.setText("") ;
out.println(requestLine);
for(int i=0; i<requestHeaders.length; i++) {
if (requestHeaders[i] == null) {
17.6.WebClient... 743

break;
} else {
out.println(requestHeaders[i] ) ;
}
}
out.println();
String line;
while ((line = in.readLine()) != null &&
!app.islnterrupted()) {
outputArea.append(line + " \ n " ) ;
}
if (app.islnterrupted() ) {
outputArea.append(" Download Interrupted ");
}
} catch(Exception e) {
outputArea.setText("Error: " + e ) ;
}
}
private boolean checkHost(String host) {
try {
InetAddress.getByName(host);
return(true);
} catch(UnknownHostException uhe) {
outputArea.setText("Bogus host: " + host);
return(false);
}
}
}

Листинг 17.15.LabeledTextField.Java

import java.awt.*; // Для FlowLayout, Font,


import javax.swing.*;
/** Поле редактирования с элементом Label. */
public class LabeledTextField extends JPanel {
private JLabel label;
private JTextField textField;
public LabeledTextField(String labelString,
Font labelFont,
int textFieldSize,
Font textFont) {
setLayout(new FlowLayout(FlowLayout.LEFT));
label = new JLabel(labelString, JLabel.RIGHT);
if (labelFont !- null) {
label.setFont(labelFont) ;
}
add(label);
textField = new JTextField(textFieldSize) ;
if (textFont != null) {
textField.setFont(textFont);
}
add(textField);
744 Глава 17. Сетевое программирование

public LabeledTextField(String labelString,


String textFieldString) {
this(labelString, null, textFieldString,
textFieldString.length(), null);
}
public LabeledTextField(String labelString,
int textFieldSize) {
this(labelString, null, textFieldSize, null);
}
public LabeledTextField(String labelString,
Font labelFont,
String textFieldString,
int textFieldSize,
Font textFont) {
this(labelString, labelFont,
textFieldSize, textFont);
textField.setText(textFieldString);

/** Компонент Label в левой части LabeledTextField.


* Для выполнения действий с Label используйте
"^ следующие операции:
* <PRE>
* LabeledTextField Itf = new LabeledTextField(...);
* Itf.getLabel().someLabelMethod(...);
* </PRE>

public JLabel getLabel() {


return(label);
}
/** Компонент TextField в правой части LabeledTextField.
V
public JTextField getTextField() {
return(textField) ;

Листинг 17.16.Interruptible.Java

/•k-k р1нтерфейс классов, которые можно опрашивать и определять,


* допускают ли они прерывания. Используется HttpClient
* и WebClient для того, чтобы дать возможность пользователю
* прерывать процесс копирования данных по сети.
V
public interface Interruptible {
public boolean islnterrupted();
}
17.7. Реализация сервера 745

ISESSCBI •а1шГх1
Hetst: jmnr. c o r e s e r v l e t s . сена

Port: (во

ReqoBsiliikeiijJGET / HTTP/I О

Ee^pest Heaiders:

J Jtf^

]f^^ „ , ', , Results


HTTF/l.e 200 OK
Date: S a t , 25 Hov 2000 0 0 : 3 5 : 1 4 GKI
S e r v e r : ] l p a c h e / l . 3 . 3 (Unix) F H P / 3 . 0 . 1 1 F r o n t P a g e / 4 . 0 . 4 . 3
ё
iContent-Type: t e x t / h t m l
l « e : 15
Via: HTTP/1.0 c l u s t e r . I n h . m d ( T r a t f i c - S e r v e r / 4 . 0 . 0 [dteSlW])

kiDOCTCTE HTML PUBLIC »-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E H " >


|<HTHL>
<HE]a»
<TITLE>Core S e r v l e t s and JavaServer Pages (JSP)</TITLE>

^
Interrupt Dowttload

Рис. 17.1. Взаимодействие с сервером www.coreservlets.com

17.7. Реализация сервера


Сервер -- это программа, которая запускается раньше клиента и ожидает запроса
на установление соединения. Реализация сервера включает следующие действия.
1. Создание объекта S e r v e r S o c k e t .
2. Создание объекта S o c k e t посредством S e r v e r S o c k e t .
3. Создание входного потока для получения информации от клиента.
4. Создание выходного потока для передачи информации клиенту.
5. Организация обмена с входным и выходным потоками.
6. Закрытие объекта S o c k e t по окончании сетевого обмена.
Подробно перечисленные действия описаны ниже. Как и для клиента, большин­
ство методов, описанных в данном разделе, могут генерировать исключение
l O E x c e p t i o n , поэтому их вызовы должны помещаться в блок t r y / c a t c h .

С о з д а н и е о б ъ е к т а ServerSocket
Клиентское гнездо предназначено ддя установления взаимодействия с конкрет­
ным сервером. В отличие о т клиентского, гнездо сервера ожидает поступления за­
просов, поэтому при создании гнезда данного типа задается только порт; адрес уз­
ла не указывается.
746 Глава 17. Сетевое программирование

ServerSocket listenSocket =
new S e r v e r S o c k e t (portNuinber) ;
В системе UNIX непривилегированным пользователям доступны номера портов,
превышающие 1023, а поскольку номера в диапазоне 1024-5000 уже используются,
номер порта должен быль больше 5000. Кроме того, необходимо просмотреть со­
держимое файла / e t c / s e r v i c e s , чтобы убедиться, что выбранный вами номер
порта не конфликтует с номерами портов, используемых другими службами. Если
вы попытаетесь принимать запросы по порту, который уже используется, будет
сгенерировано исключение l O E x c e p t i o n .

С о з д а н и е о б ъ е к т а Socket п о с р е д с т в о м ServerSocket
Многие серверы поддерживают множественные соединения, продолжая прини­
мать запросы до тех пор, пока выполняются определенные условия. Метод
a c c e p t класса S e r v e r S o c k e t ожидает установления соединения, после чего воз­
вращает обычный объект S o c k e t .
while(someCondition) {
Socket server = listenSocket.accept();
doSomethingWith(server);
}
Если вы собираетесь обеспечить несколько одновременных соединений, вы долж­
ны передавать гнезда, возвращаемые методом a c c e p t , отдельным потокам. В сле­
дующем разделе мы рассмотрим программу, в которой поддержка каждого соеди­
нения производится в отдельном потоке.

С о з д а н и е в ы х о д н о г о п о т о к а д л я п е р е д а ч и и н ф о р м а ц и и клиенту
Получив объект S o c k e t , его надо использовать приблизительно так же, как и в
клиент-программе, представленной в листинге 17.1. В приведенных ниже фраг­
ментах кода входной поток создается раньше, чем выходной, поскольку большин­
ство серверов сначала принимают данные от клиента, а затем передают ответ. Вы
можете изменить порядок создания потоков либо отказаться от создания одного
из них, если ваш сервер предназначен только для передачи информации.
Как было сказано при обсуждении клиент-программы, для чтения данных лучше
всего подходит поток Buf f e r e d R e a d e r .
BufferedReader in =
new B u f f e r e d R e a d e r
(new I n p u t S t r e a m R e a d e r ( s e r v e r . g e t l n p u t s t r e a m ( ) ) ) ;
Java также позволяет использовать O b j e c t l n p u t S t r e a m для получения сложных
объектов от других Java-программ. Объект O b j e c t l n p u t S t r e a m , связанный с
гнездом, работает так же, как и подобный объект, связанный с файлом: вам доста­
точно вызвать метод r e a d O b j e c t и привести полученный результат к требуемому
типу. Подробно сериализация объектов была рассмотрена в главе 13. Высокоуров­
невый интерфейс, использующий механизм сериализации для передачи объектов
по сети, будет обсуждаться в конце данной главы.
17.7. Реализация сервера 747

Создание выходного потока для передачи информации клиенту


Для передачи двоичных данных можно использовать универсальный объект
Outputs t г earn. Если же вы хотите воспользоваться знакомыми вам методами
p r i n t H p r i n t l n , следует создать объект P r i n t W r i t e r . .
P r i n t W r i t e r out =
new P r i n t W r i t e r ( s e r v e r . g e t O u t p u t S t r e a m О ) ;
Если клиент также написан на языке Java и ожидает передачи сложных Java-
объектов, при создании сервера можно воспользоваться средствами класса
Obj ectOutputStream.

Организация обмена с входным и выходным потоками


Объекты BufferedReader, D a t a l n p u t S t r e a m и P r i n t W r i t e r могут быть ис­
пользованы так же, как и в клиент-программе. Для чтения символов и строк класс
Buf f e r e d R e a d e r предоставляет разработчику методы r e a d и r e a d L i n e . Для чте­
ния отдельных байтов и байтовых массивов можно применять методы readByte и
r e a d F u l l y класса D a t a l n p u t S t r e a m . При работе с объектом P r i n t W r i t e r для
передачи байтов и байтовых массивов можно воспользоваться методами p r i n t и
println.

Закрытие объекта Socket по окончании сетевого обмена


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

Пример универсального сервера


Программа, представленная в листинге 17.17, реализует описанный выше подход.
Метод l i s t e n ожидает установления соединения, а затем передает гнездо методу
handleConnection, в теле которого происходит обмен данными. При создании ре­
ального сервера надо переопределить метод h a n d l e C o n n e c t i o n , чтобы реализовать
требуемое поведение программы, кроме того, если сервер должен поддерживать од­
новременную работу с несколькими клиентами, методы h a n d l e C o n n e c t i o n должны
выполняться в отдельных потоках. Простейший вариант h a n d l e C o n n e c t i o n , рас­
сматриваемый здесь, лишь сообщает имя узла, на котором расположен клиент, уста­
новивший соединение, отображает первую строку символов, принятую от клиента,
передает клиенту ответ (строку символов "Generic Network Server") и разрывает со­
единение (закрывает гнездо).

Листинг 17.17.NetworkServer.Java

import j a v a . n e t . * ;
import j a v a . i o . * ;
/** Заготовка для создания сервера. Для выполнения реальных
* задач надо переопределить метод handleConnection. Код
748 Глава 17. Сетевое программирование

* метода listen в большинстве случаев остается без изменений.


* Чтобы упростить создание PrintWriter и BufferedReader,
* NetworkServer использует SocketUtil.

public class NetworkServer {


private int port, maxConnections;

/** Сервер ожидает обращения по указанному порту. После


* установки соединения управление передается методу
* handleConnection. Работа сервера продолжается до
* получения специальной команды (System.exit) либо
* до тех пор, пока не будет обработано заданное число
* обращений. Значение О переменной maxConnections
* указывает на то, что сервер должен работать
* неограниченно долго.

public NetworkServer(int port, int maxConnections) {


setPort(port);
setMaxConnections(maxConnections);
}
/** Ожидание обращения по указанному порту. При установлении
* соединения вызывается метод handleConnection.
V
public void listen О {
int i=0;
try {
ServerSocket listener = new ServerSocket. (port) ;
Socket server;
while((i++ < maxConnections) i| (maxConnections = = 0 ) ) {
server = listener.accept();
handleConnection(server);
}
} catch (lOException ioe) (
System.out.println("lOException: " + ioe) ;
ioe.printStackTrace();
}
}

/•• в этом методе выполняются основные действия, необходимые


* для решения задачи, поставленной перед сервером. При
'^ создании реального сервера <В> необходимо
* переопределить этот метод.</В>
* <р>
* Данная версия сообщает имя узла, установившего соединение,
* отображает первую строку запроса клиента и передает
* ответ, состоящий из одной строки.

protected void handleConnection(Socket server)


throws lOException{
BufferedReader in = SocketUtil.getReader(server);
PrintWriter out = SocketUtil.getWriter(server);
17.7. Реализация сервера 749

System.out.println
("Generic Network Server: got connection from " +
server .getlnetAddress 0 .getHostName 0 + "\n" +
"with first line '" + in.readLine() + " ' " ) ;
out.println("Generic Network Server");
server.close();
}
/** Возвращает максимальное число соединений, которые
* может установить сервер. Значение О указывает на то,
* что сервер должен работать до тех пор, пока его
* выполнение не будет принудительно завершено.

public int getMaxConnectionsО {


return(maxConnections) ;

/** Установка максимального числа соединений.


* Значение О указывает на то, что сервер должен
* работать до тех пор, пока его выполнение не будет
* принудительно завершено.
V
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
/** Порт, no которому сервер ожидает обращения. */

public int getPortO {


return(port);
}
/•k-k Установка номера порта. <В Сделать это необходимо перед
* вызовом метода connect.</В> Обычно эта операция
* выполняется в теле конструктора.
V
protected void setPort(int port) {
this.port = port;

Класс N e t w o r k S e r v e r T e s t использует N e t w o r k S e r v e r для приема обращений по


указанному порту. Код класса приведен в листинге 17.18.

Листинг 1 7 . 1 8 . N e t w o r k S e r v e r T e s t . J a v a

public c l a s s NetworkServerTest {
public s t a t i c void m a i n ( S t r i n g [ ] args)
i n t p o r t = 8088;
i f ( a r g s . l e n g t h > 0) {
port = Integer.parseint(args[0]);
}
750 Глава 17. Сетевое программирование

NetworkServer nwServer = new NetworkServer(port, 1 ) ;


nwServer.listen();
}
}

Обработка обращения Web-броузера


предположим, что программа, показанная в листинге 17.18, запущена на узле
s y s t e m l . com и ожидает обращения через порт 8080.
systeml> Java NetworkServerTest
При обращении к серверу стандартного Web-броузера (в данном случае Netscape),
выполняющегося на узле system2 . com, сервер выведет следующие данные:
Generic Network Server:
got connection from system2.com
with first line 'GET /foo/ HTTP/1.0'

Взаимодействие NetworkClient и NetworkServer


Мы проверили работу классов N e t w o r k C l i e n t и NetworkServer, при этом кли­
ент-программа обращалась к стандартному FTP-серверу, а сервер обрабатывал запро­
сы стандартного Web-броузера. Теперь мы можем организовать взаимодействие этих
программ. Изменения в исходный код вносить не требуется, достаточно указать имя
узла и порт. Сервер запускается на узле s y s t e m l . c o m и ожидает обращения через
порт 6001, а клиент выполняется на узле system2 . com. При работе данных программ
отображаются следующие результаты.

Время to, systeml:


systeml> Java NetworkServerTest 6001

Время tj, system2:


system2> Java NetworkClientTest systeml.com 6001

Время tg, systeml:


Generic Network Server:
got connection from system2.com
with f i r s t l i n e 'Generic Network C l i e n t '

Время tj, system2:


Generic Network Client:
Made connection to systeml.com and got 'Generic Network Server' in
response
17.8. Пример простого HTTP-сервера 751

17.8. Пример простого HTTP-сервера


В листинге 17.19 показан код класса NetworkServer, адаптированный для работы
в качестве HTTP-сервера. Этот сервер обладает одной особенностью: вместо того
чтобы передавать указанные файлы, он возвращает полученную от клиента информа­
цию. Для этого он сохраняет полученные строки символов, затем оформляет их в ви­
де HTML-файла и передает серверу. На первый взгляд, написание программ, которые
формируют HTML-код, может показаться необычным занятием, но прочитав главы
19 и 24 вы увидите, что такие программы создаются достаточно часто. Программа,
которая действует как HTTP-сервер и возвращает полученную информацию, может
применяться в качестве инструмента отладки при создании сервлетов и JSP. В после­
дующих главах мы неоднократно будем использовать ее.

Листинг 17.19.EchoSarver.Java

import j a v a . n e t . ^ ;
import j a v a . i o . * ;
import J a v a . u t i l . S t r i n g T o k e n i z e r ;
/** Простой HTTP-сервер, который генерирует Web-страницу,
* содержащую данные, полученные от Web-клиента (обычно
* это броузер). Чтобы использовать данный сервер, его
* надо запустить в системе, задав номер порта (по умолчанию
* 8088). Предположим, что сервер запущен по адресу server.com.
* Затем надо запустить Web-броузер и обратиться с его
* помощью по адресу h t t p : / / s e r v e r . c o m : 8 0 8 8 / w h a t e v e r .
* Web-страница, сгенерированная сервером, содержит данные,
* полученные от броузера. При отладке сервлетов или
* CGI-программ надо задать значение атрибута ACTION
* дескриптора FORM h t t p : / / s e r v e r . c o m : 8 0 8 8 / w h a t e v e r .
* При обращении к серверу для передачи данных может быть
* выбран метод GET или POST.
V
p u b l i c c l a s s EchoServer extends NetworkServer {
p r o t e c t e d i n t maxRequestLines = 50;
p r o t e c t e d S t r i n g serverName = "EchoServer";
/** Номер порта передается посредством параметра командной
* строки. В противном случае используется порт 8088.
V
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
i n t p o r t = 8088;
i f ( a r g s . l e n g t h > 0) {
try {
port = Integer.parseint(args[0] ) ;
} catch(NumberFormatException nfe) {}
}
new EchoServer(port, 0);
}
p u b l i c E c h o S e r v e r ( i n t p o r t , i n t maxConnections) {
s u p e r ( p o r t , maxConnections);
752 Глава 17. Сетевое программирование

listen();
}
/•• Метод handleConnection объекта NetworkServer
* переопределяется. В переопределенном методе
* производится чтение строк символов, переданных
* клиентом. Строки символов сохраняются в массиве,
* а затем помещаются в состав элемента PRE
* HTML-страницы и передаются серверу.
V
public void handleConnection(Socket server)
throws IOException{
System.out.println
(serverName + ": got connection from " +
server.getlnetAddress().getHostName());
BufferedReader in = SocketUtil.getReader(server);
PrintWriter out = SocketUtil.getWriter(server);
String[] inputLines = new String[maxRequestLines];
int i;
for (i=0; i<maxRequestLines; i++) {
inputLines[i] = in.readLine();
if (inputLines[i] == null) // Клиент закрыл соединение.
break;
if (inputLines[i] .length () == 0) { // Пустая строка,
if (usingPost(inputLines)) {
readPostData(inputLines, i, in);
i = i + 2;
}
break;
}
}
printHeader(out);
for (int j=0; j<i; j++) {
out.println(inputLines[j]);
}
printTrailer(out);
server.close() ;
}
// Передача HTTP-ответа и заголовка стандартной Web-страницы.
// Протокол HTTP 1.0 используется для обеспечения совместимости
/ / с о всеми клиентами.
private void printHeader(PrintWriter out) {
out.println
("HTTP/1.0 200 OK\r\n" +
"Server: " + serverName + "\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"<HTML>\n" +
"<!DOCTYPE HTML PUBLIC " +
"\"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" +
"<HEAD>\n" +
" <TITLE>" + serverName + " Results</TITLE>\n" +
"</HEAD>\n" +
"\n" +
17.8. Пример простого HTTP-сервера 753

"<BODY BGC0L0R=\"#FDF5E6\">\n" +
"<Н1 ALIGN=\"CENTER\">" + serverName +
" Results</Hl>\n" +
"Here is the request line and request headers\n" +
"sent by your browser:\n" +
"<PRE>");
}
// Вывод окончания Web-страницы.
private void printTrailer(PrintWriter out) {
out.println
("</PRE>\n" +
"</BODY>\n" +
"</HTML>\n");
}
// Большинство клиентов запрашивают Web-страницы методом GET,
/ / в этом случае сервер может выполнять построчное чтение
// заголовка. Однако в HTML-формах часто задается метод
// POST; в этом случае необходимо определить число байтов,
// следующих за стандартным HTTP-заголовком.

private boolean usingPost(String[] inputs) {


return(inputs[0] .toUpperCase () .startsWith("POST"));
}
private void readPostData(String[] inputs, int i,
BufferedReader in)
throws lOException {
int contentLength = contentLength(inputs);
char[] postData = new char[contentLength];
in.read(postData, 0, contentLength);
inputs[++i] = new String(postData, 0, contentLength);
}
// Поиск поля заголовка Content-Length и получение
// целочисленного значения, содержащегося в этом поле.

private int contentLength(String[] inputs) {


String input;
for (int i=0; i<inputs.length; i++) {
if (inputs[i].length 0 == 0)
break;
input = inputs[i].toUpperCase0;
if (input.StartsWith("CONTENT-LENGTH"))
return(getLength(input));
}
return (0);
}
private int getLength(String length) {
StringTokenizer tok = new StringTokenizer(length);
tok.nextToken();
return(Integer.parseint(tok.nextToken()));
754 Глава 17. Сетевое программирование

На рис. 17.2 показаны результаты выполнения E c h o S e r v e r , в частности в окне


отображаются строки заголовка, переданные броузером Netscape 4.7, выполняющим­
ся в среде Windows 98.

мзшм
^^^Bookmi^ Jff', fioto:jhrtp //vww cofewebpfogrammmg com-8088/top/selling/iava/book/'
m
" ^ ^ ^ " W h a f » Belated

EchoServer Results
Here is the request line and request headers sent by your browser:

GET /top/selling/java/book/ HTTP/1.0


Connection: Keep-Alive
User-Agent: K o z i l l a / 4 . 7 [en] (Win98; I)
Host: www. corewebprograratning.com: 8088
Accept: irnage/gif, image/x-xbitmap, image/jpeg, iinage/pjpeg, iniage/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8

gfpyr ;DoajrJ«(nt Oof«s •jj^ .IWi*, .-iu^. Ш .t^J 4

Рис. 17.2. Сервер EchoServer возвращает данные, переданные броузером

Сервер, выполняющийся в многопотоковом


режиме
п р и работе E c h o S e r v e r возникают проблемы, связанные с тем, что данный сер­
вер может одновременно обрабатывать только одно соединение. Предположим, что
для установления соединения серверу требуется 0.001 с, для получения запроса от
клиента— 0.01 с и для передачи ответа также 0.01 с. Таким образом, время взаимодей­
ствия с клиентом составляет приблизительно 0.02 с и, следовательно, сервер может
обработать 50 запросов в секунду. Если же каждое гнездо будет поддерживаться в от­
дельном потоке, то сервер сможет обрабатывать около 1000 запросов в секунду.
В листинге 17.20 показано, как о ф о р м и т ь E c h o S e r v e r в виде многопотоковой
программы. Вопросы преобразования однопотокового метода для работы в несколь­
ких потоках рассматривались в главе 16. В новой версии программы метод
h a n d l e C o n n e c t i o n запускает поток, из которого вызывается исходный вариант
h a n d l e C o n n e c t i o n . П р и этом надо решить, каким образом будет передаваться объ­
ект S o c k e t из метода h a n d l e C o n n e c t i o n методу r u n . Если поместить этот объект в
переменную экземпляра, могут возникнуть "гонки". Для хранения объекта S o c k e t
используется класс C o n n e c t i o n , который представляет собой класс T h r e a d , под­
вергшийся незначительным изменениям.

Листинг 1 7 . 2 0 . T h r e a d e d E c h o S a r v e r . J a v a

import java.net.
import java.io.^
/ * * Многопотоковая в е р с и я E c h o S e r v e r . */
17.8. Пример простого HTTP-сервера 755

public class ThreadedEchoServer extends EchoServer


implements Runnable {
public static void main(String[] args) {
int port == 8088;
if (args.length > 0) {
try {
port = Integer.parseint(args[0]) ;
} catch(NumberFormatException nfe) {}
}
ThreadedEchoServer echoServer =
new ThreadedEchoServer(port, 0 ) ;
echoServer.serverName = "Threaded EchoServer";
}

public ThreadedEchoServer(int port, int connections) {


super(port, connections);
}
/** Новый вариант метода handleConnection, при выполнении
* которого запускается поток. В созданном потоке вызывается
* <1>старая</1> версия handleConnection, выполняющая те
* же действия, что и в однопотоковом варианте программы.
* В классе потока сохраняется объект Socket. Сделать это
* необходимо, так как методу run нельзя передавать параметры.
* Хранить объект Socket в переменной экземпляра также нельзя,
* поскольку при запуске следующего потока значение
* переменной может быть изменено.
Ч
public void handleConnection(Socket server) {
Connection connectionThread = new Connection(this, server);
connectionThread.start();
}

public void run() {


Connection currentThread =
(Connection)Thread.currentThread();
try {
super.handleConnection(currentThread.getSocket());
} catch(lOException ioe) {
System.out.println("lOException: " + ioe);
ioe.printStackTrace();
}
}
}
/•• Модификация класса Thread, дополненная полем для
* хранения объекта Socket.
V
class Connection extends Thread {
private Socket serverSocket;

public Connection(Runnable serverObject,


Socket serverSocket) {
super(serverObject);
756 Глава 17. Сетевое программирование

this.serverSocket = serverSocket;

public Socket getSocketO {


return serverSocket;
}
}

Данный сервер выполняет те же действия, что и Echo S e r v e г, но обрабатывает


одновременно несколько соединений.

17.9. RMI: Remote Method Invocation


Для поддержки распределенных объектов в Java используются средства RMI (Remote
Method Invocation — вызов удаленных методов). Если вы знакомы с технологией CORBA
(Common Object Request Broker Architecture — архитектура универсального брокера за­
просов объектов), вы можете представить себе RMI как упрощенную версию CORBA,
работающую только в среде Java. RMI также можно рассматривать как объектно-
ориентированный вариант протокола удаленного вызова процедур (RPC).
Клиент обращается к серверу для получения объекта, используя специальный вы­
сокоуровневый запрос. Получив объект, клиент вызывает его методы так же, как он
обращался бы к методам любого локального объекта. На самом деле запрос клиента
направляется удаленному серверу, который вызывает методы реального объекта и
передает результаты их выполнения обратно клиенту. Преимущество такого подхода
заключается в том, что и клиент, и сервер работают только с гнездами и с входными и
выходными потоками. Данные, которыми клиент обменивается с удаленным серве­
ром, могут представлять собой сложные Java-объекты (например, окна или другие
графические компоненты), при этом на концах соединения не приходится выполнять
никакие действия, связанные с разбором строк. Преобразование объектов выполня­
ется средствами сериализации Java.
Технология RMI настолько удобна, что может показаться странным, почему раз­
работчики до сих пор "вручную" создают программы, использующие гнезда. Но, во-
первых, средства RMI применимы только в среде Java, поэтому они не могут быть ис­
пользованы для поддержки работы клиентов и серверов HTTP, почтовых клиентов и
других приложений, в которых на другом конце соединения могут работать програм­
мы, реализованные на языках, отличных от Java. Во-вторых, даже для Java-
приложений RMI требует, чтобы на стороне клиента и на стороне сервера были уста­
новлены специальные программные компоненты. И, наконец, для работы RMI тре­
буются дополнительные ресурсы, поскольку на компьютере должны выполняться два
экземпляра виртуальной машины Java: один для средств, поддерживающих запросы,
другой для реального объекта.
Ниже описаны действия, необходимые для создания RMI-приложения. После об­
суждения этих действий мы рассмотрим четыре примера использования RMI.
1. Простой RMI-пример, в процессе работы которого удаленный объект возвра­
щает строку сообщения.
2. Пример, в котором удаленный объект выполняет численное интегрирование.
17-9. RMI: Remote Method Invocation 757

3. Расширение примера с численным интегрированием для корпоративной сис­


темы. Использование файла политики защиты и применение HTTP-сервера
для копирования RMI-файлов.
4. RMI-аплет, взаимодействующий с удаленным объектом.

Создание RMI-приложения
Для использования средств RMI в приложении вам надо создать четыре класса и
скомпилировать пять компонентов программы. Необходимые классы и действия по
компиляции кратко описаны ниже.

Необходимые классы
Для использования RMI необходимо создать четыре основных класса.
1. И н т е р ф е й с к удаленнолсу объекту. Данный интерфейс используется как кли­
ентом, так и сервером.
2. RMI-клиент. Данный клиент выполняет поиск объекта на удаленном сервере,
приводит объект к типу, определяемому интерфейсом, созданным на предыду­
щем шаге, а затем обращается к объекту как к локальному. Заметьте, что сете­
вое соединение должно поддерживаться все время, пока существует ссылка на
удаленный объект. Когда ссылка на удаленный объект обрабатывается проце-
д)^рой "сборки мусора", соединение автоматически разрывается.
3. Реализация удаленного объекта. Удаленный объект, используемый сервером,
должен реализовывать интерфейс, созданный на первом шаге.
4. RMI-сервер. Данг1ый класс создает экземпляр объекта, построенного на пре­
дыдущем шаге, и регистрирует объект, используя конкретный URL.

Компиляция и запуск системы


После того как четыре основных класса построены, для того чтобы приложение
можно было реально использовать, следует выполнить следующие пять действий,
связанных с компиляцией компонентов.
1. Компиляция клиента и сервера. На данном этапе автоматически компилиру­
ется интерфейс и реализация удаленного объекта.
2. Генерация заглушки на стороне клиента и каркаса— на стороне сервера.
Заглушка клиента и каркас сервера поддерживают вызов методов и обеспечи­
вают развертывание (marshalling) параметров. (Развертыванием называют ко­
дирование, не зависящее от устройства, и процесс сериализации, предназна­
ченные для передачи объекта через байтовый поток.) П р и генерации заглушки
и каркаса применяется компилятор r m i c .
На стороне клиента должны выполняться класс клиента, класс интерфейса и
класс заглушки. Если клиент представляет собой аплет, эти три класса должны
быть доступны на компьютере, на котором размещается этот аплет.
На стороне сервера должны присутствовать класс сервера, удаленный интер­
фейс и реализация объекта, а также класс каркаса. Заметьте, что Java 2 не тре-
758 Глава 17. Сетевое программирование

бует, чтобы на стороне сервера выполнялся каркас. Если и клиент, и сервер ра­
ботают на платформе Java 2, задается опция - v l . 2 компилятора r m i c .
3. Запуск RMI-peecTpa. Данное действие выполняется единожды, независимо от
количества удаленных объектов. Текущая версия RMI требует, чтобы реестр
выполнялся в той же системе, что и сервер.
4. Запуск сервера. Сервер запускается на той же машине, что и реестр.
5. Запуск клиента. Клиент может быть запущен на любой машине.

Простой пример использования RMI


Ниже рассматривается простой пример работы с RMI. Удаленный объект возвра­
щает сообщение в виде строки. Пример, выполняющий более сложные действия, бу­
дет рассмотрен ниже в этой главе.

Примеры необходимых классов


1. И н т е р ф е й с к удаленному объекту.
И н т е р ф е й с должен представлять собой расширение J a v a . r m i . R e m o t e ; все
методы могут генерировать исключение J a v a . r m i . R e m o t e E x c e p t i o n . При­
мер интерфейса представлен в листинге 17.21.

Листинг 17.21.Rem.java

i m p o r t J a v a . r m i . •^;
/•• RMI-клиент непосредственно использует этот интерфейс.
* RMI-сервер создает реальный удаленный объект, который
* реализует этот интерфейс, а затем регистрирует экземпляр
* объекта.
V
public interface Rem extends Remote {
public String getMessageO throws RemoteException;
}

2. RMI-клиент.
Данный класс должен выполнять поиск объекта на удаленном узле, используя
N a m i n g . l o o k u p , приводить объект к требуемому типу и обращаться к нему как
к локальному объекту. В отличие от CORBA, RMI-клиент должен знать адрес уз­
ла, на котором расположены распределенные службы. URL задается в формате
rmi ://узел/путь или rmi: //узел:порт/путь. Если номер порта не указан, ис­
пользуется порт 1099. Данный процесс может генерировать три исключения:
R e m o t e E x c e p t i o n , N o t B o u n d E x c e p t i o n и M a l f o r m e d U R L E x c e p t i o n . Их об­
работку необходимо предусмотреть в блоке t r y / c a t c h . Для обработки
M a l f o r m e d U R L E x c e p t i o n надо импортировать пакет j a v a . n e t . * . Кроме то­
го, многие клиенты передают удаленному объекту объекты S e r i a l i z a b l e , по­
этому желательно всегда импортировать пакет j a v a . i o . *, даже если в данном
конкретном случае он не используется. П р и м е р RMI-клиента приведен в лис­
тинге 17.22.
17.9. RMI: Remote Method Invocation 759

Листинг 17.22. RemClient. javS

import java.rmi.*; // Для Naming, RemoteException и т.д.


import java.net.*; // Для MalformedURLException
import java.io.*; // Для интерфейса Serializable

/** Получение объекта Rem с удаленного узла. Методы


* Rem используются так же, как и соответствующие методы
* локального объекта.
Ч
public class RemClient {
public static void main(String[] args) {
try {
String host =
(args.length > 0) ? args[0] : "localhost";
// Получение удаленного объекта и сохранение его
/ / в переменной remObject:
Rem remObject =
(Rem)Naming.lookup("rmi://" + host + "/Rem");
// Вызов методов удаленного объекта посредством remObject:
System.out.println(remObject.getMessage());
} catch(RemoteException re) {
System.out.println("RemoteException: " + re);
} catch(NotBoundException nbe) {
System.out.println("NotBoundException: " + nbe);
} catch(MalformedURLException mfe) {
System.out.println("MalformedURLException: " + mfe) ;
}
}

3. Реализация удаленного объекта.


Класс удаленного объекта должен быть подклассом UnicastRemoteObject и
реализовывать интерфейс удаленного объекта, определенный ранее. Конст­
руктор может генерировать исключение RemoteException. Пример реализа­
ции удаленного объекта приведен в листинге 17.23.

Листинг 17.23. Remlmpl. java

import Java.rmi.*;
import Java.rmi.server.UnicastRemoteObject;

/** Реализация Rem, используемая RMI-сервером. Сервер создает


* экземпляр класса, а затем регистрирует его под
* конкретным URL.
V
public class Remlmpl extends UnicastRemoteObject
implements Rem {
public Remlmpl() throws RemoteException {}

public String getMessage() throws RemoteException {


760 Глава 17. Сетевое программирование

return("Неге i s а remote message.");


}
}

RMI-сервер.
Задача сервера состоит в создании объекта и регистрации его по определенно­
му URL. Для регистрации объекта используется метод Naming, r e b i n d (он от­
меняет выполненное ранее связывание) или метод N a m i n g . b i n d (если объект
был связан ранее, генерируется исключение A l r e a d y B o u n d E x c e p t i o n ) . Тер­
мин "связывание" имеет разные значения в RMI и CORBA. В данном случае
"связывание" означает регистрацию и выполняется не клиентом, а сервером.
В блоке t r y / c a t c h , в котором выполняется связывание, необходимо преду­
смотреть обработку исключений R e m o t e E x c e p t i o n и MalformedURL-
E x c e p t i o n . Пример RMI-сервера приведен в листинге 17.24.

Листинг 1 7 . 2 4 . Remlmpl. j a v a

import java.rmi.*;
import java.net.*;
/ * * Сервер с о з д а е т объект Remlmpl (реализующий интерфейс Rem),
* з а т е м р е г и с т р и р у е т е г о под URL Rem.
V
p u b l i c c l a s s RemServer {
public s t a t i c void main(String[] args) {
try {
Remlmpl l o c a l O b j e c t = new R e m l m p l ( ) ;
Naming.rebind("rmi:///Rem", l o c a l O b j e c t ) ;
} catch(RemoteException re) {
System.out.println("RemoteException: " + re);
} c a t c h ( M a l f o r m e d U R L E x c e p t i o n mfe) {
System.out.println("MalformedURLException: " + mfe);

Компиляция и запуск примера


Как было сказано выше в этом разделе, компиляцию и запуск системы можно раз­
бить на пять этапов. Для данного примера вы должны запустить RMI-реестр, сервер
(RemServer) и клиент ( R e m C l i e n t ) в одном и том же каталоге. Если клиент и сервер
находятся в разных каталогах, то, согласно протоколу RMI, для того, чтобы загрузить
заглушку, требуется S e c u r i t y M a n a g e r . Конфигурация RMI для запуска клиента и
сервера на различных узлах или в различных каталогах будет рассмотрена далее.
На з а м е т к у

Для того чтобы рассматриваемый пример выполнялся корректно,


RMI-реестр, сервер и клиент должны быть запущены из одного ка­
талога.
1 7 . 9 . R M I : R e m o t e M e t h o d Invocation 761

1. Компиляция клиента и сервера.


Следующая команда компилирует и н т е р ф е й с Rem.
Prompt> j a v a c R e m C l i e n t . J a v a
Приведенная ниже команда выполняет компиляцию реализации объекта Remlmpl.
Prompt> j a v a c RemServer.Java
2. Генерация заглушки на стороне клиента и каркаса на стороне сервера.
Следующая команда предназначена для создания R e m I m p l _ S t u b . c l a s s и
RemImpl_Skeleton.class.
Prompt> rmic Remlmpl
или
Prompt> rmic - v l . 2 Remlmpl (для платформы J a v a 2)
Клиенту требуются файлы классов Rem. c l a s s , R e m C l i e n t . c l a s s и Remlmpl_
S t u b . c l a s s , a серверу— R e m . c l a s s , R e m l m p l . c l a s s , R e m S e r v e r . c l a s s и
RemImpl_Skeleton.class.
При работе на платформе Java 2 R e m I m p l _ S k e l e t o n . c l a s s не требуется. Для
того чтобы сгенерировать для платформы Java 2 только заглушку, надо при вызо
ве компилятора RMI указать опцию - v l . 2 . При этом будет создан файл заглушки,
который соответствует протоколу RMI 1.2, используемому платформой Java 2. По
умолчанию r m i c создает заглушки и каркасы, совместимые как с протоколом RMI
1.2, так и с более ранним протоколом RMI 1.1, используемым B J D K 1.1.
На з а м е т к у

Если клиент и сервер выполняются на платформе Java 2, для компи- ^^^^Л^


ляции интерфейса используется команда rmic -"^1.2. При этом ШШ^Ш/к
файл каркаса не генерируется. ^^Я^^Р

3. Запуск RMI-peecTpa.
Для запуска RMI-реестра используется следующая команда:
Prompt> rmiregistry
В системе UNIX в конце строки можно добавить символ "&", чтобы поддержка
реестра выполнялась как фоновый процесс. В Windows данная команда прини­
мает вид s t a r t r m i r e g i s t r y . Вы также можете указать порт, в противном
случае по умолчанию будет принят номер порта 1099.

4. Запуск сервера.
Сервер запускается по следующей команде:
S e r v e г > Java RemServer
Как и при запуске RMI-реестра, в конце строки можно добавить символ "&",
чтобы сервер выполнялся как фоновый процесс. В Windows данная команда
принимает вид s t a r t J a v a R e m S e r v e r .
762 Глава 17. Сетевое программирование

5. Запуск клиента.
Для этого надо задать приведенную ниже команду.
Proinpt> Java RemClient
Here i s a remote message.

Сервер, выполняющий численное интегрирование


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

У fix)
Определение f (х) предоставляет объект Eva l u a t a b l e (листинг 17.26). Второй
метод, i n t e g r a t e , использует метод прямоугольников для аппроксимации следую­
щего интеграла:
stop
J f(x)dx

x=8tart +steps ize/2

y^evalObj .evaluate(x) -

stepSiize
x= start x=stop

Рис. 17.3. Метод integrate аппроксимирует набором прямоугольников


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

Листинг 17.25. I n t e g r a l . Java

/** Класс для выполнения численного интегрирования. Функция


* аппроксимируется набором прямоугольников.
*/
public class Integral {
/** Возвращает сумму f(х) от x = s t a r t до x=stop, где
* функция f определяется методом e v a l u a t e объекта
* Evaluatable.

public static double згдт(double start, double stop,


double StepSize,
17.9. RMI: Remote Method Invocation 763

Evaluatable evalObj) {
double sum = 0.0, current = starts-
while (current <= stop) {
sum += evalObj.evaluate(current);
current += stepSize;
}
return(sum);
}

/** Возвращает приближенное значение интеграла f(x) от точки


* start до точки stop. Функция f определяется методом
* evaluate объекта Evaluatable и аппроксимируется
* прямоугольниками.
V
public static double integrate(double start, double stop,
int numSteps,
Evaluatable evalObj) {
double StepSize = (stop - start) / (double)numSteps;
start = start + stepSize / 2.0;
return(stepSize * sum(start, stop, stepSize, evalObj));

Листинг 17.26. Evaluatable. Java

/•* Интерфейс для вычисления значения функции у = f(х) в


* конкретной точке. Как х, так и у являются числами с
* плавающей точкой двойной точности.
V
public interface Evaluatable {
public double evaluate(double value);

Предположим, что в вашем распоряжении есть мощная рабочая станция, способ­


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

Четыре класса, необходимых для реализации


примера
в данном разделе мы продемонстрируем коды четырех классов, которые нужны
для того, чтобы рабочая станция могла работать в качестве сервера численного ин­
тегрирования.
764 Глава 17. Сетевое программирование

1. И н т е р ф е й с Remotelntegral.
В листинге 17.27 показан интерфейс, используемый как клиентом, так и сервером.

Листинг 1 7 . 2 7 . R e m o t e l n t e g r a l . J a v a

import java.rmi.*;

/•к-к Интерфейс для удаленного объекта численного интегрирования. */

public interface Remotelntegral extends Remote {

public double sum(double start, double stop, double stepSize,


Evaluatable evalObj)
throws RemoteException;

public double integrate(double start, double stop,


int numSteps, Evaluatable evalObj)
throws RemoteException;

2. Клиент Remotelntegral.
В листинге 17.28 показан код RMI-клиента. Он получает объект R e m o t e l n t e g r a l
с указанного узла, а затем использует его для приближенного вычисления инте­
грала. Заметьте, что экземпляры E v a l u a t a b l e ( S i n , Cos, Q u a d r a t i c ) помимо
интерфейса E v a l u a t a b l e реализуют интерфейс S e r i a l i z a b l e и, следователь­
но, могут передаваться по сети. Классы S i n , Cos и Q u a d r a t i c показаны соответ­
ственно в листингах 17.29-17.31. Метод t o S t r i n g каждого из трех классов будет
использован в дальнейшем при создании примера RMI-аплета.

Листинг 1 7 , 2 8 . R e m o t e l n t e g r a l C l i e n t . J a v a

import java.rmi.*;
import java.net.*;
import java.io.*;

/ * * Данный к л а с с выводит р е з у л ь т а т ы ч и с л е н н о г о и н т е г р и р о в а н и я
* с р а з л и ч н о й т о ч н о с т ь ю . Реальные вычисления выполняются
* на удаленной машине, а д р е с которой з а д а е т с я в командной
* строке.
V

public class RemotelntegralClient {


public static void main(String[] args) {
try {
String host = (args.length > 0) ? args[0] : "localhost";
Remotelntegral remotelntegral =
(Remotelntegral)Naming.lookup("rmi://" + host +
"/Remotelntegral");
for(int steps=10; steps<-10000; steps*=10) {
System.out.printIn
17.9. RMI: Remote Method Invocation 765

("Approximated with " + steps + " steps:" +


"\n Integral from 0 to pi of sin(x)=" +
remotelntegral.integrate(0.0, Math.PI,
steps, new Sin()) +
"\n Integral from pi/2 to pi of cos(x)=" +
remotelntegral.integrate(Math.PI/2.0, Math.PI,
steps, new Cos()) +
"\n Integral from 0 to 5 of x"^2=" +
remotelntegral.integrate(0.0, 5.0, steps,
new Quadratic())};
}
System.out.println
("'Correct* answer using Math library:" +
"\n Integral from 0 to pi of sin(x)=" +
(-Math.cos(Math.PI) - -Math.cos(0.0)) +
"\n Integral from pi/2 to pi of cos(x)=" +
(Math.sin(Math.PI) - Math.sin(Math.PI/2.0)) +
"\n Integral from 0 to 5 of x^2=" +
(Math.pow(5.0, 3.0) / 3.0));
} catch(RemoteException re) {
System.out.println("RemoteException: " + re) ;
} catch(NotBoundException nbe) {
System.out.println("NotBoundException: " + nbe);
} catch(MalformedURLException mfe) {
System.out.println("MalformedURLException: " + mfe);

Листинг 17.29.Sin.Java

import Java.io.Serializable;

/** Класс для вычисления sin(x), реализующий интерфейс


Evaluatable.
Ч
class Sin implements Evaluatable, Serializable {
public double evaluate(double val) {
return(Math.sin(val));

public String toStringO {


return("Sin");
766 Глава 17. Сетевое программирование

Листинг 17.30. Cos. Java

import Java.io.Serializable;

/** Класс для вычисления cos(x), реализующий интерфейс


Evaluatable.
V
class Cos implements Evaluatable, Serializable {
public double evaluate(double val) {
return(Math.cos(val));
}
public String toStringO {
return("Cosine");
}
}

Листинг 17.31. Quadratic. Java

import Java.io.Serializable;

/** Класс для вычисления x'^2, реализующий интерфейс


Evaluatable.
V
class Quadratic implements Evaluatable, Serializable {
public double evaluate(double val) {
return(val * val);
}
public String toStringO {
return("Quadratic");
}
}

3. Реализация Remotelntegral.
В листинге 17.32 показана реализация интерфейса Remotelntegral. В ней ис­
пользуются методы класса Integral.

Листинг 17.32. Remotelntegrallmpl. Java

import j ava.rmi.*;
import Java.rmi.server.UnicastRemoteObject;

/** Реализация интерфейса Remotelntegral. */

public class Remotelntegrallmpl extends UnicastRemoteObject


implements Remotelntegral {
/** Конструктор генерирует исключение RemoteException. */
17.9. RMI: Remote Method Invocation 767

public Remotelntegrallmpl() throws RemoteException {}


/** Возвращает сумму f(x) от x=start до x=stop, где
* функция f определяется методом evaluate объекта
* Evaluatable.
V
public double sum(double start, double stop, double stepSize,
Evaluatable evalObj) {
return(Integral.sum(start, stop, stepSize, evalObj));
}
/** Возвращает значение интеграла f(x), где функция
* аппроксимируется прямоугольниками. Значения
* функции f вычисляются посредством метода evaluate
* объекта Evaluatable.
V
public double integrate(double start, double stop, int numSteps,
Evaluatable evalObj) {
return(Integral.integrate(start, stop, numSteps, evalObj));
}

4. Сервер Remotelntegral.
В листинге 17.33 показан код сервера, который создает объект Remotelnte­
grallmpl и регистрирует его в локальной системе под U R L Remotelntegral.

Листинг 17.33. RemotelntegralServer.Java

import java.rmi.*;
import java.net.*;
/** Создает объект Remotelntegrallmpl и регистрирует его под
* именем 'Remotelntegral', так что удаленные клиенты могут
* связываться с ним для получения результатов численного
* интегрирования. Сервер размещается на рабочей станции,
* способной выполнять операции с плавающей точкой с высокой
* скоростью.
V
public class RemotelntegralServer {
public static void main(String[] args) {
try {
Remotelntegrallmpl integral = new Remotelntegrallmpl();
Naming.rebind("rmi:///Remotelntegral", integral);
} catch(RemoteException re) {
System.out.println("RemoteException: " + re) ;
} catch(MalformedURLException mfe) {
System.out.println("MalformedURLException: " + mfe);
}
}
768 Глава 1 7 . Сетевое п р о г р а м м и р о в а н и е

Компиляция и запуск примера


На этом этапе вам необходимо вызывать из одного каталога RMI-реестр, сервер
( R e m o t e l n t e g r a l S e r v e r ) и клиент ( R e m o t e l n t e g r a l C l i e n t ) . Если клиент и сер­
вер будут запущены из разных каталогов, то, для того, чтобы загрузить заглушку, по­
требуется S e c u r i t y M a n a g e r . Конфигурация RMI для запуска клиента и сервера на
различных узлах (или из различных каталогов) будет рассмотрена далее.
На з а м е т к у

Чтобы данный пример выполнялся корректно, RMI-реестр, сервер и


клиент должны быть запущены из одного и того же каталога.

1. Компиляция клиента и сервера.


В ответ на приглашение командной строки введите следующие команды:
Prompt> j a v a c RemotelntegralClient.Java
Prompt> j a v a c RemotelntegralServer.Java
2. Генерация заглушки клиента и каркаса сервера.
В командной строке надо задать следующее выражение:
Prompt> rmic - v l . 2 Remotelntegrallmpl
Для работы клиента необходимы классы R e m o t e l n t e g r a l . c l a s s , R e m o t e ­
l n t e g r a l C l i e n t . c l a s s и R e m o t e I n t e g r a l I m p l _ S t u b . c l a s s . Для сервера
требуются R e m o t e l n t e g r a l . c l a s s , R e m o t e l n t e g r a l l m p l . c l a s s и Remote­
l n t e g r a l S e r v e r . c l a s s . Если и клиент, и сервер используют JDK 1.1, то, что­
бы кроме заглушки был сгенерирован также и каркас Remote I n t e g ­
r a l I m p l _ S k e l e t o n , надо указать опцию - v l . 1.
3. Запуск RMI-peecTpa.
Для запуска RMI-реестра используется следующая команда:
Prompt> rmiregistry
4. Запуск сервера.
В командной строке введите следующее выражение:

Prompt> Java R e m o t e l n t e g r a l S e r v e r
5. Запуск клиента.
После ввода указанной ниже команды вы получите следующий результат:
Prompt> Java RemotelntegralClient
Approximated with 10 steps:
Integral from 0 to pi of sin(x)=2.0082484079079745
Integral from pi/2 to pi of cos (x)=-1.0010288241427086
Integral from 0 to 5 of x"2=41.5625
Approximated with 100 steps:
Integral from 0 to pi of sin(x)=2.0000822490709877
Integral from pi/2 to pi of cos(x)=-1.000010280911902
17.9. RMI: Remote Method Invocation 769

Integral from 0 to 5 of x"2=41.665624999999906


Approximated with 1000 steps:
Integral from 0 to pi of sin(x)=2.0000008224672983
Integral from pi/2 to pi of cos(x)=-1.000000102808351
Integral from 0 to 5 of x"2=41.666656249998724
Approximated with 10000 steps:
Integral from 0 to pi of sin(x)=2.00000000822436
Integral from pi/2 to pi of cos(x)=-1.0000000010278831
Integral from 0 to 5 of x^2=41.666666562504055

'Correct* answer using Math library:


Integral from 0 to pi of sin(x)=2.0
Integral from pi/2 to pi of cos (x)=-0.9999999999999999
Integral from 0 to 5 of x'"2=41. 666666666666664
Реальные значения интеграла таковы:
п
\sm{x)dx = 2
о
п
\ cos{x)dx = -1
г/2
Jt/2
55 2

При уменьшении шага разбиения результаты численного интегрирования будут


приближаться к точным значениям интегралов. Преимущество R M I состоит в том,
что вы можете переместить компоненты, выполняющие интеграцию, на более мощ­
ный сервер.

Конфигурация средств ЯМ1для использования


в корпоративной системе
При рассмотрении данного примера предполагалось, что RMI-реестр, клиент и
сервер запускаются на одном узле. П р и этом все преимущества распределенных вы­
числений, поддерживаемых RMI, теряются. Если же клиент и сервер расположены на
разных узлах, для удаленной загрузки RMI-классов необходимо, чтобы на стороне
клиента и на стороне сервера выполнялся диспетчер защиты. RMI-реестр и сервер
должны выполняться на одном узле; r m i r e g i s t r y лишь разрешает регистрировать
удаленные объекты с локального узла.
В дополнение к диспетчеру защиты, для того, чтобы позволить клиенту устанавли­
вать соединение с уделенными узлами, надо изменить политику защиты. Ниже мы
продемонстрируем действия, необходимые для использования RMI в среде предпри­
ятия, где клиент и сервер располагаются на различных машинах.
Для удаленной загрузки классов надо установить R M I S e c u r i t y M a n a g e r , как пока­
зано в коде модифицированного клиента R e m o t e I n t e g r a l C l i e n t 2 (листинг 17.34).
System.setSecurityManager(new RMISecurityManager());
770 Глава 1 7 . С е т е в о е п р о г р а м м и р о в а н и е

На з а м е т к у

В среде предприятия rmireglstry и сервер должны запускаться на


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

Листинг 17.34.RemoteIntegralClient2.Java

import java.rmi.*;
import java.net.*;
import java.io.*;

/** Данный класс представляет собой версию Java 2


* RemotelntegralClient. SecurityManager позволяет
* клиенту обращаться к удаленным машинам за
* файлами заглушки и выполнять численное интегрирование
* посредством удаленного объекта.
V
public class RemoteIntegralClient2 {
public static void main(String[] args) {
try {
System.setSecurityManager(new RMISecurityManager());
String host =
(args.length > 0) ? args[0] : "localhost";
Remotelntegral remotelntegral =
(Remotelntegral)Naming.lookup("rmi://" + host +
"/Remotelntegral") ;
for(int steps=10; steps<=10000; steps*=10) {
System.out.printIn
("Approximated with " + steps + " steps:" +
"\n Integral from 0 to pi of sin(x)=" +
remotelntegral.integrate(0.0, Math.PI,
steps, new Sin()) +
"\n Integral from pi/2 to pi of cos(x)=" +
remotelntegral.integrate(Math.PI/2.0, Math.PI,
steps, new Cos()) +
"\n Integral from 0 to 5 of x''2=" +
remotelntegral.integrate(0.0, 5.0, steps,
new Quadratic()));
}
System.out.println
("'Correct' answer using Math library:" +
"\n Integral from 0 to pi of sin(x)=" +
(-Math.cos(Math.PI) - -Math.cos(0.0)) +
"\n Integral from pi/2 to pi of cos(x)=" +
(Math.sin(Math.PI) - Math.sin(Math.PI/2.0)) +
"\n Integral from 0 to 5 of x^2=" +
(Math.pow(5.0, 3.0) / 3 . 0 ) ) ;
} catch(RemoteException re) {
System.out.println("RemoteException: " + re);
17.9. RMI: Remote Method Invocation 771

catch(NotBoundException nbe) {
System.out.println("NotBoundException: " + nbe);
catch(MalformedURLException mfe) {
System.out.println("MalformedURLException: " + mfe)

Чтобы разрешить динамическую загрузку удаленных классов, в Java 2 помимо дис­


петчера защиты требуется файл политики. В нем необходимо указать, с какими узла­
ми и по каким портам клиент может устанавливать соединения. В листинге 17.35 по­
казан файл политики, который разрешает клиенту обращаться для загрузки файлов
заглушки к r m i r e g i s t r y и серверу, выполняющимся на узле rmihost, а также к HTTP-
серверу webhost.
По умолчанию r m i r e g i s t r y ожидает обращения через порт 1099. П р и поиске
удаленного объекта клиент в первую очередь взаимодействует с r m i r e g i s t r y , ис­
пользуя указанный порт. После этого клиент связывается с сервером через тот порт,
по которому ожидает обращения сервер. Заметьте, что когда сервер регистрирует
удаленный объект, для связи с RMI-реестром выбирается произвольный порт в диапа­
зоне 1024-65535. Следовательно, для того, чтобы разрешить соединение с r m i h o s t , в
файле политики должен быть задан указанный выше диапазон портов. Классы за-
тлутки часто располагаются на HTTP-сервере, поэтому этот сервер должен быть уч­
тен в файле политики.

Листинг 17.35. Файл политики для 1слиента ( r m i c l i e n t .policy)

grant {
// rmihost - RMI-реестр и сервер
// webhost - HTTP-сервер для классов заглушки
permission Java.net.SocketPermission
"r^iihost: 1024-65535", "connect";
permission java.net.SocketPermission
"webhost:8 0", "connect";

Политика защиты указывается в командной строке при вызове клиента.


Java - D j a v a . s e c u r i t y . p o l i c y = r m i c l i e n t . p o l i c y RemoteIntegralClient2
Если выражения, приведенные в файле политики, добавить в файл J a v a . p o l i c y ,
используемый виртуальной машиной Java, можно не указывать файл политики в ко­
мандной строке. ДляJDK 1.3 файл J a v a . p o l i c y находится в каталоге / r o o t / j d k l . 3 /
lib/security/.
При запуске сервера и регистрации удаленного объекта вам также необходимо ука­
зать расположение HTTP-сервера, с которого должны загружаться файлы заглушки. Как
и в случае с файлом политики, расположение HTTP-сервера задается в командной стро­
ке; для этого используется системное свойство j a v a . r m i . s e r v e r . c o d e b a s e .
Java -Djava.rmi.server.codebase=http://webhost:port/directory/
RemotelntegralServer
772 Глава 17. Сетевое программирование

В данном случае свойство j a v a . r m i . s e r v e r . c o d e b a s e сообщает серверу о том,


что файлы заглушки, необходимые клиенту, должны быть загружены с HTTP-сервера
по адресу h t t p : / / w e b h o s t : p o r t / d i r e c t o r y / . Если HTTP-сервер использует порт,
отличный от 80, номер порта должен быть задан явно.

Компиляция и запуск примера для использования


в корпоративной системе
1. Компиляция клиента и сервера.
В ответ на приглашение командной строки введите следующие команды:

Prompt> j a v a c RemoteIntegralClient2.Java
Prompt> j a v a c RemotelntegralServer.Java
2. Генерация заглуп1ки клиента и каркаса сервера.
В командной строке надо задать следующее выражение:
Prompt> rmic - v l . 2 Remotelntegrallmpl
3. Размещение файлов на соответствующих узлах.
В табл. 17.1 описано расположение файлов классов. На стороне клиента долж­
ны находиться R e m o t e I n t e g r a l C l i e n t 2 , а также классы R e m o t e l n t e g r a l ,
S i n , Cos и Q u a d r a t i c . Поскольку последние три класса являются подклассами
E v a l u a t a b l e , а этот класс не копируется с сервера, E v a l u a t a b l e также дол­
жен находиться на клиент-машине. Кроме того, клиенту необходим файл поли­
тики r m i p o l i c y . c l i e n t .
На стороне сервера, где создается и регистрируется экземпляр удаленного объ­
екта, необходимы классы R e m o t e l n t e g r a l S e r v e r , Remotelntegrallmpl,
R e m o t e l n t e g r a l (дочерний класс) и E v a l u a t a b l e (последние три класса не ко­
пируются с HTTP-сервера). Класс I n t e g r a l необходим на сервере, поскольку
статические методы I n t e g e r , sum и I n t e g e r . e v a l u a t e вызываются в
R e m o t e l n t e g r a l l m p l . При регистрации удаленного объекта серверу также тре­
буется класс Remote I n t e g r a l Imp 1_S t u b . И наконец, на сервере необходимы
S i n , Cos и Q u a d r a t i c . В этих классах определяют метод e v a u l a t e , объявлен­
ный в интерфейсе E v a l u a t a b l e ; они требуются при динамическом связывании
объекта I n t e g r a l . Определение этих классов не передается с клиент-машины.
На HTTP-сервере должен находиться файл заглушки Remote I n t e g r a l -
Imp I S t u b , предназначенный для копирования на клиент-машину. Кроме то­
го, на HTTP-сервер следует поместить связанные с I n t e g r a l I m p l классы
R e m o t e l n t e g r a l и E v a l u a t a b l e . Они используются при регистрации удален­
ного объекта, так как r m i r e g i s t r y не получает эти файлы от сервера, соз­
дающего объект.
1 7 . 9 . R M I : R e m o t e M e t h o d Invocation 773

Таблица 1 7 . 1 . Расположение файлов классов

Клиент Сервер HTTP-сервер


RemoteIntegralClient2 RemoteIntegralServeг RemoteIntegral
Impl_Stub
RemoteIntegral RemoteIntegrallmpl RemoteIntegral
Evaluatable RemoteIntegralImp1_Stub Evaluatable
Sin RemoteIntegral
Cos Integral
Quadratic Evaluatable
Sin
Cos

Quadratic

4. Запуск HTTP-сервера.
Ha НТТР-сервере следует разместить Remote I n t e g r a l S t u b . c l a s s , R e m o t e -
I n t e g e r a l . c l a s s и E v a l u a t a b l e . c l a s s и проверить, что эти файлы дос­
тупны с помощью броузера.
5. Запуск RMI-peecxpa.
Запуск RMI-реестра выполняется по следующей команде:
Server> /somedirectory/rmiregistry
Перед запуском r m i r e g i s t r y убедитесь в том, что файлы классов отсутствуют
в каталоге, из которого вы запускаете реестр, а также в том, что на них не ука­
зывает переменная окружения CLASS PATH.

Внимание!

Если RMI'peecTp имеет доступ к файлам классов посредством


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

6. Запуск сервера.
Для запуска сервера надо задать следующую команду:
Server> J a v a -Djava.rmi.server.codebase=http://webhost/rmi/
RemoteIntegralServer
При этом предполагается, что файл заглушки находится на НТТР-сервере
w e b h o s t в каталоге r m i . Заметьте, что сервер должен запускаться на том же уз­
ле, что и r m i r e g l s t r y , но из другого каталога. Если при запуске сервера гене-
774 Глава 1 7 . С е т е в о е п р о г р а м м и р о в а н и е

рируется исключение, выясните причины проблемы и перед повторным запус­


ком сервера перезапустите RMI-реестр.

На з а м е т к у

RMI'peecTp и сервер должны выполняться на одном узле, в против­


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

7. Запуск клиента.
Для того чтобы получить выходные данные, приведенные ниже, надо задать в
командной строке следующее выражение (здесь rmihost— это узел, на кото­
ром выполняются rmiregistry и сервер):
Client> Java -DJava.security.policy=rmiclient.policy
RemGteIntegralClient2 rmihost
Approximated with 10 steps:
Integral from 0 to pi of sin(x)=2.0082484079079745
Integral from pi/2 to pi of cos(x)=-1.0010288241427086
Integral from 0 to 5 of x^2=41.5625
Approximated with 100 steps:
Integral from 0 to pi of sin(x)=2.0000822490709877
Integral from pi/2 to pi of cos(x)=-1.000010280911902
Integral from 0 to 5 of x"2=41.665624999999906
Approximated with 1000 steps:
Integral from 0 to pi of sin(x)=2.0000008224672983
Integral from pi/2 to pi of cos (x)=-1.000000102808351
Integral from 0 to 5 of x^2-41.666656249998724
Approximated with 10000 steps:
Integral from 0 to pi of sin(x)=2.00000000822436
Integral from pi/2 to pi of cos(x)=-1.0000000010278831
Integral from 0 to 5 of x"2=41.666666562504055
'Correct' answer using Math library:
Integral from 0 to pi of sin(x)=2.0
Integral from pi/2 to pi of cos(x)=-0.9999999999999999
Integral from 0 to 5 of x"2=41.666666666666664

Пример RMI-annera
Создание аплета, использующего RMI-взаимодействие, намного проще по сравне­
нию с созданием аналогичного приложения, поскольку с аплетом всегда связан дис­
петчер защиты, управляющий загрузкой удаленных файлов, поэтому RMI-аплету не
нужен RMISecurityManager. С другой стороны, на работу аплета накладывается ряд
ограничений. Так, аплет может устанавливать сетевое соединение только с сервером,
с которого он был загружен, поэтому RMI-реестр, сервер, регистрирующий удален­
ные объекты, и HTTP-сервер, с которого аплет копирует файл заглушки, должны рас­
полагаться на одном узле.
Раз.мещая аплет на HTTP-сервере, убедитесь, что файлы клиента расположены в
том же каталоге, что и файл класса аплета. Вы также можете расположить JAR-файл в
каталоге аплета и указать его посредством атрибута ARCHIVE дескриптора APPLET.
Подробно о создании JAR-файлов рассказывалось в разделе 7.10.
17.9. RMI: Remote Method Invocation 775

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


удаленным объектом посредством RMI. Поскольку RMI-реестр и сервер располагают­
ся на узле, с которого был скопирован аплет, имя узла в RMI U R L определяется по­
средством вызова getCodeBase () .getHost (). Результаты выполнения аплета в
броузере Netscape 6 показаны на рис. 17.4.

Листинг 17.36. RemotelntegralApplet. Java

import java.awt.*;
import Java.awt.event.*;
import java.rmi.*;
import java.net.*;
import java.io.*;
import javax.swing.*;
/** Данный класс представляет собой версию RemotelntegralClient,
* выполненную в виде аплета. Аплет обращается к удаленной
* машине для выполнения численного интегрирования.
* Поскольку аплет работает с собственным диспетчером
* компоновки, для загрузки классов заглушки необходимость
* в RMISecurityManager отпадает.
V
public class RemotelntegralApplet extends JApplet
implements ActionListener {
private Evaluatable[] shapes;
private Remotelntegral remotelntegral;
private JLabel result;
private JTextField startlnput, stoplnput, steplnput;
private JComboBox combo;
public void initO {
String host = getCodeBase().getHost();
try {
remotelntegral =
(Remotelntegral)Naming.lookup("rmi://" + host +
"/Remotelntegral");
} catch(RemoteException re) {
reportError("RemoteException: " + re);
} catch(NotBoundException nbe) {
reportError ("NotBoundException: " -i- nbe);
} catch(MalformedURLException mfe) {
reportError("MalformedURLException: " + mfe);
}
Container context = getContentPane();
// Создание раскрывающегося списка.
shapes = new Evaluatable[]{ new Sin(),
new Cos(),
new Quadratic!) };
combo = new JComboBox(shapes);
context.add(combo, BorderLayout.NORTH);
// Ввод входных данных,
startlnput = new JTextField();
stoplnput = new JTextField0;
776 Глава 17. Сетевое программирование

steplnput = new JTextField();


result = new JLabelO;
JPanel labelPanel = new JPanel(new GridLayout(4,1)
labelPanel.add(new JLabel("Start:"));
labelPanel.add(new JLabel("Stop:"));
labelPanel.add(new JLabel("Steps:"));
labelPanel.add(new JLabel("Result: " ) ) ;
context.add(labelPanel, BorderLayout.WEST);
JPanel inputPanel = new JPanel(new GridLayout(4,1) ;
inputPanel.add(startlnput);
inputPanel.add(stoplnput);
inputPanel.add(steplnput);
inputPanel.add(result);
context.add(inputPanel, BorderLayout.CENTER);
// Установка кнопки.
JPanel buttonPanel = new JPanel(new FlowLayout());
JButton submit = new JButton("Submit");
submit.addActionListener(this);
buttonPanel.add(submit);
context.add(buttonPanel, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent event) (
try {
int steps = Integer.parseint(steplnput.getText0);
double start = Double.parseDouble(startlnput.getText());
double stop = Double.parseDouble(stoplnput.getText0);
showStatus("Calculating . . . " ) ;
Evaluatable shape = (Evaluatable)combo.getSelectedItem()
double area = remotelntegral.integrate(start, stop,
steps, shape);
result.setText(Double.toString(area));
showStatus("");
} catch(NumberFormatException nfe) {
reportError("Bad input: " + nfe);
} catch(RemoteException re) {
reportError("RemoteException: " + re);
}
}
private void reportError(String message) {
System.out.println(message);
showStatus(message);
}

шшалз:^ .:^тщ
J Ffle Edit Vievv Search Go i

Quadratic
i t ^ i,
^ 1
0 0
i^b^i 5 0
iU'-u 1000
b > , ^ < ' 11 f ^?'<<^ъ ^.,.,>,..
Suhmtt Рис. 17.4. Так выглядит аплет, взаимодействующий
с удаленным объектом посредством RMI, в окне броузера
r r r : : i . '::| Netscape 6
17.9. RMI: Remote Method Invocation 777

Броузер Netscape (кроме версии 6) и все версии Internet Explorer без установки
продукта Java Plug-In (см. раздел 9.9) не поддерживают Java 2 и протокол RMI 1.2. Бо­
лее того, Internet Explorer без установки дополнительного модуля не поддерживает
даже протокол RMI 1.1. Дополнительный модуль для работы с RMI доступен по адресу
f t p : / / f t p / m i c r o s o f t . c o m / d e v e l o p r / m s d n / u n s u p - e d / . Протокол RMI 1.1 под­
держивается в Netscape 4.06 и более поздних версиях.

17.10. Резюме
Механизм гнезд, реализованный в Java, позволяет создавать сетевые клиенты и
серверы, взаимодействующие с сетевыми программами, написанными на любом язы­
ке программирования. Процесс создания клиента включает следующие действия: соз­
дание гнезда, связывание с гнездом входного и выходного потоков, использование
этих потоков при сетевом взаимодействии и закрытие гнезда. Сервер действует ана­
логично, но перед созданием гнезда он ожидает установления соединения. П р и соз­
дании как клиента, так и сервера приходится решать задачу разбора строк символов.
В этом существенную помощью может оказать класс S t r i n g T o k e n i z e r . Для под­
держки распределенной обработки удобно использовать технологию RMI.
Следующая часть данной книги посвящена созданию программ, выполняющихся
на стороне сервера. В первую очередь мы рассмотрим HTML-формы, с помощью ко­
торых создается и н т е р ф е й с для взаимодействия с программами на стороне сервера,
такими как сервлеты и CGI-сценарии. С помощью ф о р м осуществляется взаимодей­
ствие с пользователем и передача полученных от него данных на сервер. После этого
мы обсудим Java-сервлеты, которые выполняются на Web-сервере и играют роль по­
средников межд)' Web-броузерами или другими HTTP-клиентами и базами данных ли­
бо приложениями, также находящимися на стороне сервера. После этого речь пойдет
о технологии JavaSei-\'er Pages (JSP), которая позволяет включать в статические
HTML-страницы динамические фрагменты. Мы также расскажем вам о написании
аплетов, предназначенных для передачи данных HTTP-серверам, расположенным за
брандмауэрами. Кроме того, мы рассмотрим JDBC API, с помощью которого вы може­
те передавать SQL-запросы базам данных. И, наконец, вы узнаете о средствах Java
API, предназначенных для обработки XML-файлов.
-Jzj^-j^±

ПРОГРАММЫ,
ВЫПОЛНЯЮЩИЕСЯ
НА СТОРОНЕ
СЕРВЕРА
В ЭТОЙ части...

Глава 18. НТМЬ-формы


Глава 19. Java на стороне сервера: сервлеты
Глава 20. JavaServer Pages
Глава 21. Аплеты как интерфейс к программам на стороне сервера
Глава 22. JDBC
Глава 23. Обработка ХМЬ-документов
HTML-ФОРМЫ

В ЭТОЙ главе...

Передача данных, введенных посредством формы.


Элемент FORM.
Интерфейсные элементы, предназначенные для ввода текста.
Кнопки.
Флажки и переключатели опций.
Раскрывающиеся списки и окна списков.
Управляющий элемент, предназначенный для копирования
файлов.
Карты изображений на стороне сервера.
Скрытые поля.
Группировка интерфейсных элементов.
Порядок выбора элементов.
Sly\ZJ^^

Д о сих пор мы рассматривали Java-приложения и Java-аплеты, выполняющиеся


на стороне клиента. Однако очень важной с ф е р о й применения Java является
использование дарпюго языка д,ля создания так называемых серверов "среднего
)ровня", выполняющихся на стороне Web-сервера. Основными технологиями созда­
ния программ, работающих на стороне сервера, являются Java-сервлеты и JavaSen^er
Pages. Однако, прежде чем приступить к обработке данных, надо выяснить, как про­
исходит их получение и передача на сервер.

1 8 . 1 . Передача данных с помощью


HTML-форм
HTML-формы позволяют создавать различные и н т е р ф е й с н ы е элементы и прини­
мать информацию от пользователя. С каждым из элементов связывается имя и значе­
ние. Имя задается посредством HTML-кода, а значение может быть указано в HTML-
документе либо сформировано в результате действий пользователя. Форме ставится в
соответствие URL программы, которая предназначена для обработки данных, вве­
денных пользователем. Когда пользователь активизирует форму (обычно это осуще­
ствляется щелчком мыши на специальной кнопке), имена и значения управляющих
элементов передаются по адресу, указанному в форме, в следующем виде:
имя1=значение15симя2=значение25с. . . &имяЫ=значениеМ
Данная строка параметров передается указанной программе одним из двух спосо­
бов. Согласно первому из них (он соответствует HTML-методу GET), строка присое­
диняется к URL в составе запроса. Для разделения URL и строки параметров между
ними помещается знак вопроса. Второй способ соответствует методу POST. В этом
сл)'Чае серверу передается строка запроса POST, за ней поля заголовка и пустая стро­
ка, после чего след)'ет строка параметров.
В качестве примера в листинге 18.1 приведен HTML-код документа, содержащего два
поля редактирования. Внешний вид этого документа показан на рис. 18.1. HTML-
782 Глава 18. HTML-формы

элементы, использованные для создания этой формы, будут рассмотрены ниже в этой
главе. Сейчас нас интересуют лишь некоторые особенности данной Web-страницы. Во-
первых, заметьте, что одному из полей редактирования присвоено имя f i r s t N a m e , а
второму— l a s t N a m e . Во-вторых, обратите Внимание! на то, что интерфейсные элемен­
ты являются элементами текстового уровня и чтобы добиться требуемого расположе­
ния полей редактирования относительно текста, надо включать в состав Web-страницы
дескрипторы, предназначенные для форматирования. И, наконец, примите к сведе­
нию, что с формой связан URL h t t p : / / l o c a l h o s t : 8 0 8 8 / S o m e P r o g r a m , по которому
будут передаваться данные, введенные пользователем.

Листинг 1 8 . 1 . GetForm.html

<IDOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>A Sample Form U s i n g GET</TITLE>
</HEAD>

<BODY BGC0L0R="#FDF5E6">
<H2 ALIGN="CENTER">A Sample Form U s i n g GET</H2>

<FORM A C T I O N = " h t t p : / / l o c a l h o s t : 8 0 8 8 / S o m e P r o g r a m " >


<CENTER>
F i r s t name:
<INPUT TYPE="TEXT" NAME="firstName" VALUE="Joe"XBR>
L a s t name:
<INPUT TYPE="TEXT" NAME="lastName" VALUE="Hacker"XP>
<INPUT TYPE="SUBMIT"> < ? — Кнопка активизации формы — >
</CENTER>
</FORM>

</BODY>
</HTML>

Перед тем как информация, введенная посредством формы, будет впервые передана
на сервер, надо запустить на стороне сервера (в данном случае на локальном компьюте­
ре) программу E c h o S e r v e r , которую мы рассмотрели ранее в разделе 17.8. Эта про­
грамма представляет собой "мини-сервер" и может оказать существенную помощь при
отладке. Независимо от того, какой каталог и файл указаны в составе URL, E c h o S e r v e r
возвращает клиенту принятую информацию, оформленною в виде Web-страницы. Как
видно из рис. 18.2, когда пользователь введет в первом поле редактирования J o e , а во
втором поле— H a c k e r и щелкнет на кнопке Submit Query, броузер обратится к URL
h t t p : / / l o c a l h o s t : 8 0 8 8 / S o m e P r o g r a m ? f i r s t N a m e = J o e & l a s t N a m e = H a c k e r . В лис­
тинге 18.2 представлен код модифицированного документа, в котором вместо метода
GET используется метод POST; внешний вид документа показан на рис. 18.3. Как видно из
рис. 18.4, значения J o e и H a c k e r по-прежнему представляются в формате f i r s t N a m e =
J o e & l a s t N a m e = H a c k e r , но в данном случае эта строка параметров располагается после
пустой строки, разделяющий заголовок и тело Н1ТР-запроса.
18.1. Передача данных с помощью HTML-форм 783

k 4' ^- г ^А ^^ М-^-л аа Ш
•'• 'ч^^у''Вос*1тв<к$ ^ 1осгй«т |h«p//localhosl/'GetFofm html
d
А Sample Form Using GET
1 First name: p o e
1 Last name: JHacker

1 SiAmtt Query |

' ^ ==Ф^ boctiment Done ^^^ ^<^ -^


• ,^ .-•«^ , „ : J

P*fc- 78. 7. Внешний вид документа GetForm.html

Сказанное выше можно кратко выразить следующим образом. HTML-формы объе­


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

шшшшшЕяшшшшшшшт
Fie £ ( * V'tew feo CowifMiic^or йф
Si
4 ~i ё __^<
<>loc«*on-|hHp
^. i l //ЬсдХпой
,, | i " Bookmafks -J> ^£ ^
8088/'Somerruyci
"3
EchoSei4'er Results
Here is the request line and request headers sent by your browser:

GET /SoineProgram)?f irstName-JoeflastNeune-Hacker HTTP/1,0


Relerer: http://localhost/GetFor».html
Connection: Keep-Alive
User-Xgent: H o z i H a / 4 . 7 [en] (Uin98; U)
Host: localhost:8088
Accept: iniage/gif, image/x-xbitrt»ap, image/jpeg, iroage/pjpeg, iroage/png, */«
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-B

',^;.»Ф- ОосшигЛОоп*
^^ ::э -2^
Рис. 18.2. HTML-запрос, переданный документом GetForm.html,
отображаемым в броузере Netscape 4.7
784 Глава 18. HTML-формы

Листинг 18.2. PostForm.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>A Sample Form Using POST</TITLE>
</HEAD>

<BODY BGC0L0R="#FDF5E6">
<H2 ALIGN="CENTER">A Sample Form Using P0ST</H2>

<FORM ACTION="http://localhost:8088/SomeProgram"
METHOD="POST">
<CENTER>
First name:
<INPUT TYPE="TEXT" NAME="firstName" VALUE="Joe"><BR>
Last name:
<INPUT TYPE="TEXT" NAME="lastName" VALUE="Hacker"><P>
<INPUT TYPE="SUBMIT">
</CENTER>
</FORM>

</BODY>
</HTML>

UsmaPOST -Netscdoe
g o j;jc4T»mu'm-^^fof tieto сжз
ll :P ^^ i -I- ''Ж J IS:
f BiXik-i>afks ^ LcscalJon:jhftp/'/locaihos:t,''PostFofmhtni

A Sample Form Using POST


First r ;; j j o e

Last name: |Hacker

Subtnit Query

Оосигпдат* Done
^;^
Рис. 18.3. Внешний вид документа PostForm.html
18.2. Элемент FORM 785

MfirVffiliffflMiEBaMHHHHHHHHHIIIillllllllliiilillllllillli I il [
file £<* ^ie«/v g o £ommurac^tor Лф

• •/ ;• 1-?'.^* -Si ..1 rf 3 i l __Я


f'^ Botf^.Ti*!'; ,^ loc<j{iCsr) jUtpV/localhosf SOSS/SooieProgtam jrj

EchoSei^ er Results
Here is the request line and request headers sent by your browser:

POST /SotneProgram HTTP/1.0


R e f e r e r : h t t p : / / l o c a l h o s t / P o s t F o r n > . html
Connection: Keep-Alive
User-JLgent: H o z i l l a / 4 . 7 [en] (Win98; U)
Host: localhost:8088
Accept: Itaagei/gii, image/x-xbitmap, image/Jpeg, iroage/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,ut£-8
Content-type: application/x-www-form-uriencoded
Content-length: 29

firstName-Joes lastName-Hacker

^^^
Рис. 18.4. HTML-запрос, переданный документом PostForm.html,
отображаемым в броузере Netscape 4.7

18.2. Элемент FORM


HTML-формы позволяют создавать набор интерфейсных элементов, предназна­
ченных для ввода данных. В составе ф о р м ы задается URL конкретного рес)рса. С ка­
ждым из и н т е р ф е й с н ы х элементов связывается имя и значение, причем значение
может быть определено в составе HTML-документа и устанавливаться в результате
действий пользователя. П р и активизации ф о р м ы имена и значения всех элементов
объединяются в строку параметров. Имя отделяется от значения знаком равенства
("="), а пары имя-значения разделяются СР1МВОЛОМ "&". Созданная строка параметров
передается программе, URL которой указан в форме. Строка либо присоединяется к
URL (для разделения URL и строки параметров применяется символ "?"), либо по­
мещается в тело HTTP-запроса, отделенное от заголовка запроса пустой строкой.
Способ передачи строки параметров на сервер зависит от того, какое значение имеет
атрибут METHOD элемента FORM; допустимыми значениями являются GET и POST. В
данном разделе рассматривается элемент FORM; основное Внимание! уделяется связы­
ванию URL с ф о р м о й и способам передачи строки параметров серверу. В последую­
щих разделах речь поР1дет об интерфейсных элементах, включаемых в состав формы.

Элемент: <FORM ACTION="URL" ...> ... </FORM>


Атрибуты: ACTION (обязательный), METHOD, ENCTYPE, TARGET, ONSUBMIT, ONRESET,
ACCEPT, ACCEPT-CHARSET
Элемент FORM объединяет и н т е р ф е й с н ы е элементы. С ним связывается URL, оп­
ределяющий ресурс, которому передаются данные, введенные пользователем. Эле­
мент FORM имеет следующий вид:
<FORM A C T I O N = " h t t p : / / s o m e . i s p . c o m / s e r v l e t / S o m e S e r v l e t " >
Интерфейсные элементы и другие выражения HTML
</FORM>
786 Глава 18. HTML-формы

В данном разделе вы познакомитесь с атриб)пгами элемента FORM: ACTION, METHOD,


ENCTYPE, TARGET, ONSUBMIT, ONRESET, ACCEPT и ACCEPT-CHARSET. Заметьте, что
STYLE, CLASS и LANG рассматриваться не будут; речь пойдет только об атрибутах,
специфических для элемента FORM.

ACTION
Атрибут ACTION определяет URL сервлета или CGI-сценария, предназначенного для
обработки данных, передаваемых формой (например, h t t p : / / c g i . w h i t e h o u s e .
g o v / b i n / s c h e d u l e - f u n d - r a i s e r ) , либо почтового адреса, по которому должна
быть передана эта информация (например, m a i l t o : a u d i t @ i r s . gov). Некоторые
провайдеры не позволяют пользователям размещать на сервере сервлеты и CGI-
программы. В этом случае единственным способом доставки данных, введенных по­
средством формы (например, заказы на приобретение продукции), остается переда­
ча их по электронной почте. Задавая URL, начинающийся с m a i l t o , необходимо
указывать метод POST (см. описание атрибута METHOD).

METHOD
Атрибут METHOD задает способ передачи данных HTTP-серверу. При использовании
метода GET данные присоединяются через знак вопроса ("?") к URL, связанному с
формой. Пример использования этого метода приведен в разделе 18.1. По умолча­
нию броузеры, запрашивая у сервера HTML-страницы, также применяют метод GET.
При использовании метода POST данные передаются в отдельной строке.
Метод GET имеет как преимущества, так и недостатки. Этот метод очень прост, и ес­
ли программа, выполняющаяся на стороне сервера, использует метод GET, для ее от­
ладки и тестирования не обязательно применять форму, достаточно задать URL,
присоединив к нему требуемые данные. С другой стороны, из-за ограничений, на­
кладываемых броузером на размер URL, ограничивается также и объем данных, ко­
торые могут быть присоединены к нему. Метод POST свободен от этого недостатка.
Кроме того, большинство броузеров вместе с URL документа отображают также и
присоединенные данные. Это не позволяет использовать метод GET для работы с
конфиденциальными данными там, где экран дисплея виден посторонним.

ENCTYPE
Атрибут ENCTYPE определяет способ кодировки данных, предназначенных для
передачи. По умолчанию используется кодировка a p p l i c a t i o n / x - w w w - f o r m -
u r l e n c o d e d , согласно которой клиент преобразует каждый пробел в символ "-ь", а
любой символ, не являющийся латинской буквой или ц и ф р о й , в трехсимвольную
последовательность, состоящую из знака "%'' и двузначного шестнадцатеричного
числа, представляющего код символа. Знак равенства между именем и значением и
знак "&", которым разделяются пары имя-значение, не кодируются.
Например, на рис. 18.5 показан документ G e t F o r m . h t m l (листинг 18.1), в котором
в первом поле редактирования введеЕ1а строка " M a r t y ( J a v a H a c k e r ? ) " . Как
видно из рис. 18.6, на сервер будет передана последовательность символов
"Marty+%28Java+Hacker%3F%2 9". Пробелы заменены символами "+", 28— это
шестнадцатеричный код левой скобки, 3F— код знака вопроса, а 29 — код правой
скобки.
18.2. Элемент FORM 787

I ^ A Sample Fotm Using GET - Microsoft fnternel Expkuet


""жшшаявяшш
ШШ\
1 ; Ajfdwss I^E] h«p /'/localhost/GetForm html j j r^6o 'Links**

A Sample Form Using GET


1 First name: j M arty (Java H acke r?)

1 Last name; JHall

1 I Q Subnj¥5uefy^ J 1

1 ^ Local ««r^net vj

Рис. 18.5. В поле редактирования документа GetForm.html введена


строка, содержащая знаки пунктуации

Л1 ШетеА Expfotet
' file Е<Й Vbv f^voftes lods йф

- Ajjdtest 1 ^ http //localhost 8088/'SomePfogram'>fir$(Name'Marty4-^28Java-t-Hacker^3F^29t.lastName=Hall ^\ ^Go Lmks *

J
EchoSei^er Results
Here is the request Hne and request headers sent by your browser:

GET /SowePrograuM?firstNaiDe-Harty+%28Java+Hac)cer\3F%29«lastNa»e-Hall HTTP/1.1


Accept: iroage/gif, imacre/x-xbitmap, imaige/Jpeg, image/pjpeg, a p p l i c a t i o n / n e w o r c
Referer: hctp://localhosc/Getrorre.htinl
Accept-Language: e n - u s
Accept-Encoding; g z i p , d e f l a t e
User-Agent: n o z i l i a / 4 . 0 ( c o m p a t i b l e ; MSIE 5 . 0 ; Windows 98; DigExt)
Host: l o c a l h o s t : 8 0 8 8
Connection: Keep-Alive

ШОопе * ^ l o c ^ rtifdnet
J
Рис. 18.6. HTTP-запрос, переданный Internet Explorer 5.0 при акти­
визации формы, показанной на рис. 18.5

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


соответствующий значению m u l t i p a r t / f o r m - d a t a атрибута ENCTYPE. П р и этом
каждое из полей передается как отдельная часть МШЕ-совместимого документа.
При передаче автоматически выбирается метод POST. П р и использовании данно­
го способа кодирования упрощается обработка данных сложной структуры на сто­
роне сервера. Это особенно важно в тех случаях, когда форма передает на сервер
целые документы (подробно об этом — в разделе 18.7). Например, в листинге 18.3
представлена форма, которая отличается от ф о р м ы в документе G e t F o r m . h t m l
(листинг 18.1) только тем, что вместо
<FORM ACTION="http://localhost:8088/SomeProgram">
используется выражение
788 Глава 18. HTML-формы

<FORM ACTION="http://localhost:8088/SomeProgram"
ENCTyPE="multipart/form-data">
Результаты показаны на рис. 18.7 и 18.8.

Листинг 1 8 . 3 . M u l t i p a r t F o r m . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>Using E N C T Y P E = " m u l t i p a r t / f o r m ~ d a t a " < / T I T L E >
</HEAD>
<BODY BGC0L0R="#FDF5E6">
<H2 ALIGN="CENTER">Using ENCTYPE="multipart/form-data"</H2>

<FORM A C T I O N = " h t t p : / / l o c a l h o s t : 8 0 8 8 / S o m e P r o g r a m "


ENCTYPE="multipart/form-data">
<CENTER>
F i r s t name:
<INPUT TYPE="TEXT" NAME="firstName" VALUE="Joe"><BR>
L a s t name:
<INPUT TYPE="TEXT" NAME="lastName" VALUE="Hacker"><P>
<INPUT TYPE="SUBMIT">
</CENTER>
</FORM>
</BODY>
</HTML>

Ш^ Using ЕНСГУРЕо-пмАфаИ^от^Ша" Netsc


£ie £d< View fio CowtwntcaJo» ИФ

a
Using ENCTYPE=^'miiltipiirt/form-data'^
First name: j Joe
Last name: JHacker

Submit Query

Рис. 18.7. Внешний вид документа MultipartForm.html


18.2. Элемент FORM 789

-fcchoSeivef Results - Nelsc^>e n|X!


£te £cSt View go £оттип)сйо( tiefe

; -^ --^ л :^i ^ Й ^^ d' a :ii fl


EchoSei"\er Results
Here is the request line and request headers sent by your browser:
POST /SomeProgram HTTP/1.0
Referer: h t t p : / / l o c a l h o s t / H u l t i p a r t . r o r » . h t m l
Connection: Keep-Alive
User-Agent: H o z i l l a / 4 . 7 [en] (¥in98; U)
Host: localhost:8088
Accept: iroage/gif, image/x-xbitmap, image/jpeg, image/pjpeg, iroage/png, «/«
Accept-Encoding: gsip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-e
Content-type: multipart/form-data; boundary* 30247933410805
Content-Length: 253

30247933410805
Content-Disposition: form-data; name="firstName"

Joe
30247933410805
Content-Disposition: form-data; пагое*"lastName"

Hacker
302 47933 410805 —

Ш",Ч>' ^Ш::.т::.Шл:.:Я:^±

Рис. 18.8. HTTP-запрос, передаваемый броузером Netscape 4.7 при акти­


визации формы в документе MultipartForm.html

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

ONSUBMIT и ONRESET
Данные атрибуты задают JavaScnpt-код, используемый для проверки содержимого
ф о р м ы перед передачей данных серверу или сбросом содержимого интерфейсных
элементов. Если выражение, на которое указывает атрибут ONSUBMIT, возвращает
значение f a l s e , содержимое ф о р м ы не передается на сервер. Это позволяет про­
верить с помощью JavaScript-сценариев формат и значение данных, предназна­
ченных для передачи на сервер, и оповестить пользователя об ошибке.

ACCEPT и ACCEPT-CHARSET
Данные атрибуты появились только в HTML 4.0. С их помощью определяются
MIME-ТИПЫ (ACCEPT) и кодировка символов (ACCEPT-CHARSET), которые должны
поддерживаться сервлетом или другой программой, обрабатывающей информа­
цию. MIME-ТИПЫ, перечисленные в атрибуте ACCEPT, могут также быть использо­
ваны клиентом для ограничения типов файлов, доступных пользователю при ко­
пировании.
790 Глава 1 8 . H T M L - ф о р м ы

18.3. Управляющие элементы для обработки


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

Поля редактирования
Элемент: <INPUT TYPE="TEXT" NAME="..." ...> (закрывающий
дескриптор отсутствует)
Атрибуты: NAME (обязательный), VALUE, SIZE, MAXLENGTH, DISABLED, RE^ШONLY,
ONCHANGE, ONSELECT, ONFOCUS, ONBLUR, ONKEYDOWN, ONKEYPRESS, ONKEYUP
Данный элемент создает поле, предназначенное для редактирования одной стро­
ки текста. П р и м е р ы полей редактирования были приведены в листингах 18.1-18.3.
Для работы с фрагментом текста, содержащим несколько строк, используется эле­
мент TEXTAREA. Если атрибут TYPE в элементе INPUT не указан, по умолчанию при­
нимается тип TEXT, однако при создании HTML-документов рекомендуется явно за­
давать тип интерфейсного элемента. След}^ет помнить, что элементы в составе фор­
мы размещаются в окне в соответствии с правилами форматирования, поэтому при
создании ф о р м ы следите за тем, чтобы броузер не разместил и н т е р ф е й с н ы й элемент
отдельно от поясняющего текста.
М е т о д и к а профессионалов

Для размещения интерфейсных элементов и поясняющего текста


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

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


клавишу <Enter> в тот момент, когда курсор находится в поле редактирования. Одна­
ко такое поведение броузера не предусмотрено стандартами, поэтому на него не сле­
дует ориентироваться при проектировании документов. В частности, Netscape пере­
дает содержимое ф о р м ы после нажатия клавиши <Enter>, только если форма содер­
жит единственное текстовое поле. Число ф о р м в документе не имеет значения.
Internet Explorer реагирует подобным образом на нажатие клавиши <Enter> в том слу­
чае, если на странице находится только одна форма; при этом число текстовых полей
в форме не учитывается. Броузер Mosaic передает содержимое ф о р м ы серверу в том
случае, если при нажатии клавиши <Enter> к)рсор находился в последнем поле редак­
тирования на Web-странице.
Внимание!

Не следует рассчитывать на то, что после нажатия клавиши <Enter>


содержимое формы будет передано на сервер. Используйте для © в
этой цели специальную кнопку или карту изображений. %%
18.3. Управляющие элементы для обработки текста 791

Далее в этом разделе описаны атрибуты, специфические для полей редактирова­


ния. Атрибуты, общие для всех HTML-элементов, не рассматриваются. Атрибут
TABINDEX, применимый для всех элементов формы, мы обсудим в разделе 18.11.

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

VALUE
Значение атрибута VALUE определяет начальное содержимое поля редактирова­
ния. П р и активизации ф о р м ы серверу передается текущее содержимое поля, ко­
торое формируется в результате действий пользователя. Если на момент передачи
содержимого ф о р м ы поле редактирования пусто, строка параметров имеет сле­
дующий вид: прочие_данные8сtext f ieldname^Sinpo4ue_daHHue.

SIZE
Атрибут SIZE задает ширину текстового поля, которая вычисляется исходя из
средней ширины символа, отображаемого определенным шрифтом. Если количе­
ство символов превышает ширину элемента, его содержимое сдвигается влево так,
чтобы последний символ строки был виден на экране. Это также может произой­
ти, если в состав введенной строки входит много символов, ширина которых пре­
вышает среднее значение (например, прописная буква W). Очевидно, что сказан­
ное справедливо только для пропорциональных шрифтов, которые по умолчанию
использует для отображения содержимого полей редактирования броузер
Netscape. Internet Explorer использует для этой цели моноширинный шрифт, и та­
кое поведение броузера нельзя изменить, даже если поместить элемент INPUT в
состав элемента FONT или CODE.

MAXLENGTH
Атрибут MAXLENGTH задает максимально допустимое число символов в поле редак­
тирования. Это значение отличается от числа видимых символов, которое задает­
ся с помощью атрибута SIZE.

DISABLED, R E A D O N L Y
Атрибут DISABLED полностью запрещает доступ к полю редактирования, а атри­
бут READONLY переводит его в режим "только для чтения". Поле редактирования,
доступ к которому запрещен, не получает фокус ввода ни после щелчка мышью, ни
после нажатия клавиши <ТаЬ>, поэтому его содержимое не может быть изменено.
Кроме того, информация, содержащаяся в запрещенном поле редактирования, не
передается на сервер. Текст, содержащийся в поле, которое находится в режиме
"только для чтения", не может быть изменен, однако такие поля могут получать
фокус ввода и сведения о них включаются в строку параметров, передаваемую на
сервер. Несмотря на то что атрибуты DISABLED и READONLY предусмотрены спе­
цификацией HTML 4.0, броузер Netscape 4.x не поддерживает их.
792 Глава 1 8 . H T M L - ф о р м ы

O N C H A N G E , ONSELECT, O N F O C U S , O N B L U R , O N K E Y D O W N ,
ONKEYPRESS и O N K E Y U P
Указанные атрибуты используются только при работе с броузерами, поддерживающи­
ми JavaScript. Фрагменты кода, на которые указывают значения данных атрибутов, об­
рабатывают события, соответствующие изменению содержимого поля редактирова­
ния с переходом на другой элемент, выбору пользователем содержимого текстового
поля, получению и потере фокуса ввода, а также нажатию клавиш на клавиатуре.

Поле ввода пароля


Элемент: <INPUT TYPE="PASSWORD" NAME="..." ...> (закрывающий
дескриптор отсутствует)
Атрибуты: NAME (обязательный), VALUE, SIZE, MAXLENGTH, DISABLED, READONLY,
ONCHANGE, ONSELECT, ONFOCUS, ONBLUR, ONKEYDOWN, ONKEYPRESS, ONKEYUP
Единственное отличие поля ввода пароля от обычного поля редактирования за­
ключается в том, что при вводе текста пользователем вместо введенных символов
отображается специальный знак, как правило, звездочка ("*"). Внешний вид поля па­
роля показан на рис. 18.9. Данный элемент удобно использовать для ввода конфиден­
циальных данных, которые не должны быть доступны посторонним, например паро­
ля или номера платежной карточки. Введенный текст при передаче по сети не шиф­
руется. Поскольку^ при передаче методом GET данные присоединяются к URL и могут
отображаться на экране, желательно использовать для передачи пароля метод POST.
М е т о д и к а профессионалов

Чтобы обеспечить секретность данных, введенных в поле пароля,


следует использовать для их передачи метод POST.

NAME, VALUE, SIZE, MAXLENGTH, DISABLED, READONLY, O N C H A N G E ,


ONSELECT, O N F O C U S , O N B L U R , O N K E Y D O W N , ONKEYPRESS и
ONKEYUP
Атрибуты поля ввода пароля используются точно так же, как и соответствующие
атрибуты поля редактирования.

Рис. 18.9. Поле пароля, созданное с помощью


EnterPassword: ****** дескриптора <INPUTTYPE="PASSWORD" ...>

Текстовые области
Элемент:<TEXTAREANAME='..." ROWS=xxxCOLS=yyy>... </TEXTAREA>
Атрибуты: NAME (обязательный), ROWS (обязательный), COLS (обязательный), WRAP
(нестандартный), DISABLED, READONLY, ONCHANGE, ONSELECT, ONFOCUS, ONBLUR,
ONKEYDOWN, ONKEYPI^SS, ONKEYUP
Элемент TEXTAREA создает область, позволяющую работать с фрагментом текста,
состоящим из нескольких строк. Внешний вид текстовой области показан на рис. 18.10.
18.3. Управляющие элементы для обработки текста 793

В данном элементе атрибут VALUE отсутствует. В качестве исходного содержимого эле­


мента используется текст, помещенный между открывающим и закрывающим дескрип­
торами. Текст, находящийся между <TEXTAREA> и </TEXTAREA>, интерпретируется так
же, как и текст в составе использовавшегося ранее элемента ХМР: пробелы сохраняются,
а HTML-дескрипторы рассматриваются как литералы. Исключение составляют & l t ; ,
&сору; и другие подобные последовательности символов, которые преобразуются
обычным образом. Если в составе формы не указан атрибут ENCTYPE (см. раздел 18.2),
некоторые символы перед передачей серверу преобразуются. В частности, пробелы за­
меняются знаками "+", а символы, отличные от латинских букв и цифр, кодируются по­
следовательностями "%ХХ", где XX — двузначное шестнадцатеричное число, определяю­
щее код символа.

NAME
Данный атрибут определяет имя элемента.

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

COLS
Атрибут COLS задает ширину текстовой области. Ш и р и н а элемента вычисляется
исходя из средней ш и р и н ы символа в шрифте, используемой для представления
текста. Если число символов в строке превышает значение атрибута COLS, различ­
ные броузеры поступают по-разному. В Netscape отображается горизонтальная по­
лоса прокрутки (однако, с помощью атрибута WRAP вы можете изменить поведение
элемента, предусмотренное по умолчанию). В Internet Explorer слова, выходящие
за пределы области, переносятся на новую строку.

WRAP
Атрибут WRAP, специфический для броузера Netscape, определяет действия, кото­
рые должны предприниматься, если строка оказывается длиннее, чем ширина об­
ласти, указанная с помощью атрибута COLS. Значение OFF, предполагаемое по
умолчанию, запрещает перенос слов. В этом случае, чтобы перейти на новую стро­
ку, пользователь должен явно задать символ перевода строки. Значение HARD зада­
ет "жесткий" перенос слов; при передаче данных на сервер в соответствующих по­
зициях указываются символы перевода строк. Наконец, значение SOFT также раз­
решает перенос слов, но при передаче данных на сервер символы перевода строк
находятся только в тех позициях, где их ввел пользователь.

DISABLED, R E A D O N L Y
Атрибут DISABLED полностью запрещает дост)^п к текстовой области, а атрибут
READONLY переводит ее в режим только для чтения. Текстовая область, доступ к
которой запрещен, не получает фокус ввода ни после щелчка мышью, ни после
нажатия клавиши <ТаЬ>, соответственно ее содержимое не может быть изменено.
794 Глава 18. HTML-формы

Кроме того, информация, находящаяся в запрещенной текстовой области, не пе­


редается на сервер. Текст, содержащийся в области, которая находится в режиме
"только для чтения", не может быть изменен, однако такие области могут получать
фокус ввода и текст, помещенный в них, включается в строки параметров, переда­
ваемые на сервер. Несмотря на то что эти атрибуты предусмотрены спецификаци­
ей HTML 4.0, броузер Netscape 4.x не поддерживает их.

ONCHANGE, ONSELECT, ONFOCUS, ONBLUR, ONKEYDOWN,


ONKEYPRESS и ONKEYUP
Указанные атрибуты используются только при работе с броузерами, поддержи­
вающими JavaScript. Фрагменты кода, на которые указывают значения данных ат­
рибутов, обрабатывают события, связанные с текстовой областью. Атрибут
ONCHANGE соответствует изменению содержимого области с последующей потерей
этим элементом фокуса ввода. Код, на который указывает значение атрибута
ONSELECT, обрабатывает событие, возникающее при выборе пользователем со­
держимого текстовой области. Атрибуты ONFOCUS и ONBLUR определяют действия
при получении и потере фокуса ввода. Остальные атрибуты соответствуют нажа­
тию клавиш на клавиатуре.
В приведенном ниже примере создается текстовая область, рассчитанная на ото­
бражение пяти строк текста, каждая из которых может содержать до 30 символов.
Внешний вид текстовой области показан на рис. 18.10.
<CENTER>
<Р>
Enter some HTML:<BR>
<TEXTAREA NAME="HTML" R0WS=5 COLS=30>
Delete this text and replace
with some HTML to validate.
</TEXTAREA>
<CENTER>
Enter some HTML:
Delete this text and replace
with some HTML to validate.

Ш J Рис. 18.10. Текстовая область

18.4. Кнопки
Как правило, кнопки в составе форм используются для активизации формы, т.е.
для запуска процедуры передачи данных на сервер и для сброса содержимого элемен­
тов формы в состояние, заданное в HTML-документе. Броузеры, поддерживающие
JavaScript, могут также применять кнопки для запуска фрагментов JavaScript-кода.
Чаще всего кнопки создаются с помощью элемента INPUT; при этом атрибут TYPE
может принимать значения SUBMIT, RESET или BUTTON. Кроме того, в HTML 4.0 был
определен элемент BUTTON, но в настоящее время он поддерживается только Internet
Explorer. Этот новый элемент позволяет создавать кнопки с надписями, состоящими
из нескольких строк, выводить на кнопках изображения, выбирать шрифт, а также
1 8 . 4 . Кнопки 795

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


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

Внимание!

Netscape не поддерживает элемент BUTTON.

Кнопка Submit
Элемент: <INPUT TYPE="SUBMIT" ...> (закрывающий дескриптор
отсутствует)
А т р и б у т ы : NAME, VALUE, DISABLED, ONCLICK, ONDBLCLICK, ONFOCUS, ONBLUR
После щелчка на кнопке S u b m i t содержимое ф о р м ы передается сервлету или дру­
гой программе на стороне сервера, URL которой задан посредством атрибута ACTION
элемента FORM. Несмотря на то что процедура передачи данных может быть запущена
и другими способами, например с помощью карты изображений, большинство форм
содержат в своем составе хотя бы одну кнопку S u b m i t . Подобно другим интерфейс­
ным элементам, внешний вид кнопки S u b m i t зависит от операционной системы, по­
этому на различных платформах она выглядит по-разному. На рис. 18.11 показана
кнопка S u b m i t , созданная посредством выражения <INPUT TYPE="SUBMIT"> и ото­
бражаемая в системе Windows 98.

Submit Query
Рис. 18.11. Кнопка Submit с надписью, выбираемой по умолчанию

NAME и VALUE
С большинством и н т е р ф е й с н ы х элементов связываются имя и значение, которые
передаются на сервер. Поскольк}^ кнопка S u b m i t предназначена исключительно
для запуска процедуры передачи данных, атрибут NAME может отсутствовать; в
этом случае пара и м я - з н а ч е н и е не будет передаваться обрабатывающей програм­
ме. Если имя задано, на сервер передается только имя и значение активизирован­
ной кнопки. Надпись на кнопке определяется значением данного элемента.
Задавая значение посредством атрибута VALUE, вы можете изменить надпись на
кнопке. Приведенный ниже фрагмент кода создает поле редактирования и две
кнопки S u b m i t . Внешний вид ф о р м ы показан на рис. 18.12. Если пользователь
щелкнет на первой кнопке, строка параметров, передаваемая на сервер, будет
иметь вид Item=256MB+SIMM&Add=Add+Item+to+Cart.
<CENTER>
Item:
<INPUT TYPE="TEXT" NAME="Item" VALUE="256MB SIMM"><BR>
<INPUT TYPE="SUBMIT" NAME="Add"
VALUE="Add Item to Cart">
<INPUT TyPE="SUBMIT" NAME="Delete"
VALUE="Delete Item from Cart">
</CENTER>
796 Глава 18. HTML-формы

Item: |25бнв SIMH


Рис. 18.12. Кнопки Submit с надписями, которые
Add tlem to Cart Delete \tero from Cart
определяются значениями элементов

DISABLED
Атрибут DISABLED запрещает доступ к кнопке. Такая кнопка не может пол)^чать
фокус ввода, и ее имя и значение не передаются на сервер. JavaScript-сценарий,
содержащийся на Web-странице, может динамически изменять данный атрибут.
Несмотря на т о что DISABLED предусмотрен спецификацией HTML 4.0, броузер
Netscape 4.x не поддерживает данный атрибут.

ONCLICK, ONDBLCLICK, O N F O C U S и O N B L U R
Указанные атрибуты используются только при работе с броузерами, которые под­
держивают JavaScript. Фрагменты кода, на которые указывают атрибуты ONCLICK и
ONDBLCLICK, выполняются при активизации кнопки. Код, соответств)тощиР1 атрибу­
ту ONFOCUS, выполняется при получении кнопкой фокуса ввода, а код, определяе­
мый значением атрибута ONBLUR, — при потере фокуса. Если фрагмент кода, связан­
ный с кнопкой, возвращает значение f a l s e , передача данных не происходит. Имена
HTML-атрибутов не зависят от регистра символов, поэтому в реальных док)'ментах
они обычно обозначаются как o n C l i c k , o n D b l C l i c k , o n F o c u s и o n B l u r .

Элемент: <BUTTON TYPE='SUBMIT" ...>


HTML-код
</BUTTON>
Атрибуты: NAME, VALUE, DISABLED, ONCLICK, ONDBLCLICK, ONFOCUS, ONBLUR
Этим способом также можно создавать кнопки S u b m i t , однако они буд)^^ поддер­
живаться только броузером Internet Explorer. Внешний вид кнопки, созданной по­
средством элемента BUTTON, определяет HTML-код, содержащийся между откры­
вающим и закрывающим дескрипторами. Данный элемент позволяет отображать на
кнопках надписи, состоящие из нескольких строк, выбирать для этих надписей
шрифт, выводить на кнопках изображения и реализовывать другие эффекты. Пример
использования элемента BUTTON приведен в листинге 18.4, а внешний вид кнопки по­
казан на рис. 18.13.

Листинг 1 8 . 4 . B u t t o n E l e m e n t . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>The BUTTON Element</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<H2 ALIGN="CENTER">The BUTTON Element</H2>

<FORM A C T I O N = " h t t p : / / l o c a l h o s t : 8 0 8 8 / S o m e P r o g r a i n " >


<CENTER>
<BUTTON TyPE="SUBMIT">Single-line Label</BUTTON>
&nbsp;&nbsp;
18.4. Кнопки 797

<BUTTON TYPE="SUBMIT">Multi-line<BR>label</BUTTON>
<P>
<BUTTON TYPE="SUBMIT">
<B>Label</B> w i t h < I > f o n t < / I > c h a n g e s .
</BUTTON>
<P>
<BUTTON TYPE="SUBMIT">
<IMG S R C = " i m a g e s / J a v a - L o g o . g i f " WIDTH=110 HEIGHT=101
ALIGN="LEFT" ALT="Java Cup Logo">
LabeKBIO^with image
</BUTTON>
</CENTER>
</FORM>

</BODY>
</HTML>

fcl^я.llu^l^ьg;Яi^JЩ|^^P^ДJfw.'!';fЯ^ BMK.rnlxfl
' EHe m Ytew Favorites iool* ЙФ B l

J
The BUTTON Element
1 MuttHme
[| Smgte-line Label | label j

1 Le^ei wifli /(b/wtharvges |

1 i ^УЛМ
J
Щ'&опб ifJMyCoft

Рис. 18.13. Кнопка Submit, созданная с помощью


элемента BUTTON

NAME, VALUE, DISABLED, ONCLICK, ONDBLCLICK, O N F O C U S и


ONBLUR
Данные атрибуты используются так же, как и атрибуты элемента <INPUT
TYPE="SUBMIT" . . . > .

Кнопка Reset
Элемент: <INPUT TYPE="RESET" ...> (закрывающий дескриптор
отсутствует)
Атрибуты: VALUE, NAME, DISABLED, ONCLICK, ONDBLCLICK, ONFOCUS, ONBLUR
Кнопка R e s e t предназначена для сброса содержимого ф о р м ы в состояние, опре­
деляемое атрибутами VALUE элементов. Значение кнопки R e s e t никогда не передает­
ся на сервер.
798 Глава 18. HTML-формы

VALUE
Атрибут VALUE определяет надпись на кнопке. По умолчанию отображается стро­
ка "Reset".

NAME
Поскольку содержимое кнопки Reset не передается на сервер, атрибут NAME, как
правило, не указывается. Однако в документах, содержащих JavaScript-коды, атри­
бут NAME позволяет JavaScript-сценарию ссылаться на данную кнопку.

DISABLED
Данный атрибут позволяет запретить доступ к кнопке. Запрещенная кнопка не по­
лучает фокус ввода. JavaScript-сценарий, содержащийся на Web-странице, может
динамически изменять данный атрибут. Несмотря на то что атрибут DISABLED
определен спецификацией HTML 4.0, броузер Netscape 4.x не поддерживает его.

ONCLICK, ONDBLCLICK, ONFOCUS и ONBLUR


Эти нестандартные атрибуты используются при работе с броузерами, поддержи­
вающими JavaScript. Фрагменты кода, на которые указывают атрибуты ONCLICK и
ONDBLCLICK, выполняются при активизации кнопки. Код, соответствующий атри­
буту ONFOCUS, выполняется тогда, когда элемент получает фокус ввода, а код, оп­
ределяемый значением атрибута ONBLUR, — при потере фокуса. HTML-атрибуты не
зависят от регистра символов, поэтому в реальных документах они обычно обо­
значаются как onClick, onDblClick, onFocus и onBlur.

HTML Element: <BUTTON TYPE="RESET" ...>


HTML -код
</BUTTON>
А т р и б у т ы : VALUE, NAME, DISABLED, ONCLICK, ONDBLCLICK, ONFOCUS, ONBLUR
Данный способ создания кнопки Reset поддерживает только InteiTiet Explorer. HTML-
код, содержащийся между открывающим и закрывающим дескрипторами, определяет
данные, отображаемые на кнопке. Атрибуты, указываемые в составе данного дескриптора,
используются так же, как и атрибуты элемента <INPUT TYPE="RESET" . . . >.

Кнопки JavaScript
Элемент: <INPUT TYPE="BUTTON" ...> (закрывающий дескриптор
отсутствует)
А т р и б у т ы : NAME, VALUE, DISABLED, ONCLICK, ONDBLCLICK, ONFOCUS, ONBLUR
Элемент INPUT типа BUTTON распознается только броузерами, поддерживающими
JavaScript. Данный элемент создает кнопку, которая выглядит подобно кнопкам Submit
и Reset и позволяет связывать фрагменты кода с атрибутами ONCLICK, ONDBLCLICK,
ONFOCUS и ONBLUR. Имя и значение, связанные с такой кнопкой, входят в состав дан­
ных, передаваемых на сервер. С кнопкой может быть связан любой фрагмент JavaScript-
18.5. Флажки и переключатели опций 799

кода, но обычно этот код используется для проверки формата данных, содержащихся в
составе интерфейсных элементов. В приведенном ниже примере создается кнопка, при
активизации которой вызывается JavaScript-функция v a l i d a t e Form.
<INPUT TYPE="BUTTON" VALUE="Check Values"
onClick="validateForm{)">

Элемент: <BUTTON TYPE="BUTTON" ...>


HTML-код
</BUTTON>
Атрибуты: NAME, VALUE, DISABLED, ONCLICK, ONDBLCLICK, ONFOCUS, ONBLUR
Этот способ создания JavaScript-кнопок поддерживается только Internet Explorer.
HTML-код, содержащийся между открывающим и закрывающим дескрипторами, опреде­
ляет данные, отображаемые на кнопке. Атрибуты, указываемые в составе данного деск­
риптора, используются так же, как и атрибуты элемента <INPUT ТУРЕ="BUTTON" . . . >.

18.5. Флажки и переключатели опций


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

Флажки опций
Элемент: <INPUT TYPE="CHECKBOX" NAME="..." ...>
(закрывающий дескриптор отсутствует)
Атрибуты: NAME (обязательный), VALUE, CHECKED, DISABLED, READONLY, ONCLICK,
ONFOCUS, ONBLUR
Значение, связанное с флажком опции, созданным с помощью элемента INPUT
данного типа, передается на сервер только в том случае, если флажок установлен.
Следующий фрагмент кода создает флажок опции, показанный на рис. 18.14.
<Р>
<INPUT TYPE="CHECKBOX" NAME="noEmail" CHECKED>
Check here if you do <I>not</I> want to
get our email newsletter

F Check here if you do not want to get our етаД newsletter

Рис. 18.14. Флажок опции, созданный с помощью


элемента INPUT

Заметьте, что описание назначения данного интерфейсного элемента представля­


ет собой обычный HTML-текст. Чтобы текст отображался рядом с интерфейсным
элементом, надо включить в состав документа элементы, выполняющие форматиро­
вание. Так, дескриптор <Р> в предыдущем примере оформляет управляющий элемент
и пояснительный текст как один абзац.
800 Глава 1 8 . H T M L - ф о р м ы

М е т о д и к а профессионалов

Абзацы в составе контейнера FOPMформируются также, как и в дру­


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

NAME
Данный атрибут задает имя, которое при активизации ф о р м ы передается на сер­
вер. Имена стандартных флажков опций должны обязательно присутствовать, но
если элемент предназначен только для взаимодействия с JavaScript-сценарием, ат­
рибут NAME может отсутствовать.

VALUE
Данный атрибу'т указывать не обязательно. П о умолчанию принимается значение
VALUE, равное on. Как было сказано выше, имя и значение передаются на сервер
только в том случае, если флажок установлен. В предыдущем примере ф о р м а пере­
дает пару n o E m a i l = o n , но если пользователь перед активизацией ф о р м ы сбросит
флажок опции, эти данные не будут отображаться в строке параметров. Поэтому
сервлеты или CGI-программы, анализирующие информацию, переданную клиен­
том, должны лишь проверять факт наличия имени элемента указанного типа; зна­
чение можно не рассматривать.

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

DISABLED, READONLY
Атриб)^^ DISABLED запрещает доступ к элементу, а атрибут READONLY переводит
его в режим только для чтения. Несмотря на то что атрибуты DISABLED и
READONLY определены в спецификации HTML 4.0, броузер Netscape 4.x не под­
держивает их.

ONCLICK, O N F O C U S и O N B L U R
Данные атрибуты определяют фрагменты JavaScript-кода, обрабатывающие щел­
чок мышью на элементе, получение и потерю фокуса ввода.

Переключатели опций
Элемент: <INPUT TYPE="RADIO" NAME="..." VALUE="..." ...>
(закрывающий дескриптор отсутствует)
Атрибуты: NAME (обязательный), VALUE (обязательный), CHECKED, DISABLED,
READONLY, ONCLICK, ONFOCUS, ONBLUR
Переключатели опций отличаются от флажков тем, что в каждый момент времени
в группе может быть выбран только один элемент. К группе относятся все элементы
1 8 . 5 . Ф л а ж к и и переключатели опций 801

данного типа с одинаковыми именами (значениями атрибута NAME). Выбирая новый


элемент в группе, вы автоматически отменяете предыдущий выбор. В составе строки
параметров передаются имя и значение выбранного элемента. Переключатели, при­
надлежащие к одной группе, не обязательно должны быть расположены рядом, одна­
ко в большинстве случаев разработчики документов размещают их недалеко друг от
друга. Н и ж е приведен пример создания группы переключателей опций. Поскольку
элементы INPUT форматируются как обычные элементы текстового уровня, для раз­
мещения переключателей с отступом относительно общего заголовка использован
элемент DL. Внешний вид созданных переключателей опций показан на рис. 18.15.
Если пользователь не выберет новый элемент, то при активизации кнопки S u b m i t на
сервер будет передана пара c r e d i t C a r d = j a v a .
<DL>
<DT>Credit Card:
<DD><INPUT TYPE="RADIO" NAME="creditCard" VALUE="visa">
Visa
<DD><INPUT TYPE="RADIO" NAME="creditCard" VALUE="mastercard">
Master Card
<DD><INPUT TYPE="RADIO" NAME="creditCard"
VALUE="java" CHECKED>
Java Smart Card
<DD><INPUT TYPE="RADIO" NAME="creditCard" VALUE="amex">
American Express
<DD><INPUT TYPE="RADIO" NAME="creditCard" VALUE="discover">
Discover
</DL>
Credit Card:
r Visa
r Master Card
a Java Smart Card
r American Express
r Discover Рис. 18.15. Переключатели опций в HTML-документе

NAME
В отличие от большинства других элементов, одно и то же имя присваивается не­
скольким переключателям опций. Элементы с одним именем объединяются в логи-
ческ)то группу. Заметьте, что значения атрибутов зависят от регистра символов, по­
этому два переключателя, описанные ниже, будут принадлежать к разным группам.
<INPUT TYPE="RADIO" NAME="Foo" VALUE="Valuel">
<INPUT TYPE="RADIO" NAME="FOO" VALUE="Value2">

Внимание!

Значения атрибутов NAME ДЛЯ переключателей опций, принадлежа­


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

VALUE
Значение атрибута VALUE вместе со значением NAME передается на сервер в соста­
ве строки параметров. Атрибут VALUE не влияет на внешний вид переключателя.
802 Глава 18. HTML-формы

Как и для флажков опций, в качестве пояснительного текста используется обыч­


ный HTML-текст.

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

DISABLED, R E A D O N L Y
Атрибут DISABLED запрещает доступ к элементу, а атрибут READONLY переводит
его в режим "только для чтения". Несмотря на то что DISABLED и READONLY опре­
делены в спецификации HTML 4.0, броузер Netscape 4.x не поддерживает их.

ONCLICK, O N F O C U S и O N B L U R
Данные атрибуты определяют фрагменты JavaScript-кода, обрабатывающие щел­
чок мышью на элементе, получение и потерю фокуса ввода.

18.6. Раскрывающиеся списки и окна списков


Элемент SELECT предоставляет пользователю список, пункты которого тот может
выбирать. Если разрешен выбор только одного пункта и не указан размер элемента, он
представляется как раскрывающийся список. Если в дескрипторе SELECT содержатся
атрибуты, позволяющие выбирать несколько пунктов списка или задающие число
строк, отображаемых на экране, элемент представляется как окно списка. Пункты спи­
ска формируются с помощью элементов OPTION, которые помещаются между дескрип­
торами <SELECT> и </SELECT>. Формат элемента SELECT представлен ниже.
<SELECT NAME="Name" . . . >
<OPTION VALUE="Valuel">TeKCT 1-го пункта
<OPTION VALUE="Value2">TeKCT 2-го пункта

<OPTION VALUE="ValueN">TeKCT N-ro пункта


</SELECT>
В спецификации HTML 4.0 предложен элемент OPTGROUP (с единственным атри­
бутом LABEL), который использует элементы OPTION для создания каскадных меню,
однако ни Netscape, ни Internet Explorer не поддерживают его.

Элемент: <SELECT NAME="../' ...> ... </SELECT>


Атрибуты: NAME (обязательный), SIZE, MULTIPLE, DISABLED, ONCLICK, ONFOCUS,
ONBLUR, ONCHANGE
Элемент SELECT создает раскрывающиеся списки или окна списков. Каждый
пункт формируется с помощью элемента OPTION.

NAME
Атрибут NAME задает имя элемента, передаваемое сервлету или CGI-программе.
18.6. Раскрывающиеся списки и окна списков 803

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

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

DISABLED
Атрибут DISABLED запрещает доступ к списку. Несмотря на то что атрибут
DISABLED определен в спецификации HTML 4.0, броузер Netscape 4.x не поддер­
живает его.

ONCLICK, O N F O C U S , O N B L U R и O N C H A N G E
Эти нестандартные атрибуты используются при работе с броузерами, поддержи­
вающими JavaScript. О н и определяют фрагменты JavaScript-кода, которые выпол­
няются после щелчка мышью на пункте списка, при получении и потере фокуса
ввода, а также при изменении выбора с последующей потерей фокуса.

Элемент: <OPTION ...> (закрывающий дескриптор отсутствует)


Атрибуты: SELECTED, VALUE, DISABLED
Данный элемент может появляться только в составе элемента SELECT либо других
элементов, предназначенных ддя создания списков.

SELECTED
Если в составе дескриптора <OPTION> есть атрибут SELECTED, это означает, что
данный элемент должен автоматически выбираться при загрузке Web-страницы.

VALUE
Атрибут VALUE определяет значение, передаваемое серверу вместе с именем эле­
мента SELECT. Это значение не совпадает с текстом пункта. Текст пункта задается
отдельно после дескриптора <OPTION>.

DISABLED
Данный атрибут позволяет запретить доступ к пункту списка. Несмотря на то что
данный атрибут определен в спецификации HTML 4.0, броузер Netscape 4.x не
поддерживает его.
В приведенном ниже примере создается элемент SELECT, пунктами которого яв­
ляются названия языков программирования. Поскольку в дескрипторе <SELECT> от­
сутствуют атриб)ты SIZE и MULTIPLE, данный элемент отображается как раскры­
вающийся список. На рис. 18.16 и 18.17 показан внешний вид элемента сразу после за­
грузки Web-страницы и после щелчка мышью на кнопке, расположенной в правой
804 Глава 18. HTML-формы

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


формы на сервер в составе строки параметров будет передана пара l a n g u a g e = j a v a .
Заметьте, что обрабатывающей программе передается не текст пункта, а значение
атрибута VALUE.
Favorite language:
<SELECT NAME="language">
<OPTION VALUE="c">C
<OPTION VALUE="c++">C++
<OPTION VALUE-"Java" SELECTEOJava
<OPTION VALUE="lisp">Lisp
<OPTION VALUE="perl">Perl
<OPTION VALUE="smalltalk">Smalltalk
</SELECT>

Рис. 18.16. Элемент SELECT представляет


Favorite language: | Java раскрывающийся список

Favorite language: Java ^J


С 1
C++ 1
Ш99НИВН
Lisp
Perl
Smalltalk | Рис. 18.17. Пункты элемента SELECT

В следующем примере элемент SELECT представляется в виде окна списка. Если на


момент активизации формы выбрано несколько пунктов списка, серверу передается
каждое из значений атрибута VALUE (имя элемента SELECT повторяется). Если в окне
списка пункты выбраны так, как показано на рис. 18.18, при активизации формы на сер­
вер будет передана последовательность символов l a n g u a g e = j a v a & l a n g u a g e = p e r l .
Передача одного имени в составе нескольких пар имя-значение является одной из при­
чин, по которым разработчики сервлетов предпочитают метод g e t P a r a m e t e r V a l u e s
объекта H t t p S e r v l e t R e q u e s t методу g e t P a r a m e t e r . Подробно этот вопрос будет
рассматриваться в разделе 19.6.
L a n g u a g e s you know:<BR>
<SELECT NAME="language" MULTIPLE>
<OPTION VALUE="c">C
<OPTION VALUE="c++">C++
<OPTION VALUE="java" SELECTED>Java
<OPTION VALUE="lisp">Lisp
<OPTION VALUE="perl" SELECTED>Perl
<OPTION VALUE="smalltalk">Smalltalk
</SELECT>

Languages you know.


3
Lisp
Рис. 18.18. Так выглядит элемент SELECT, при определении
Smalltalk J j
которого указан атрибут MULTIPLE
1 8 . 7 . Управляющий элемент... 805

18.7. Управляющий элемент,


предназначенный для копирования
файлов
Элемент: <INPUT TYPE="FILE" ...> (закрывающий
дескриптор отсутствует)
Атрибуты: NAME (обязательный), VALUE (игнорируется), SIZE, MAXLENGTH, ACCEPT,
DISABLED, READONLY, ONCHANGE, ONSELECT, ONFOCUS, ONBLUR (нестандартный)
Данный элемент создает поле ввода имени файла и расположенную рядом с ним
кнопку Browse. Пользователь может непосредственно ввести путь к файлу в поле ре­
дактирования либо, щелкнув на кнопке, отобразить диалоговое окно выбора файла и
указать требуемый файл в интерактивном режиме. П р и активизации ф о р м ы на сер­
вер передается содержимое выбранного файла (для этого надо, чтобы з н а ч е т ^ е ат­
рибута ENCTYPE было равно m u l t i - p a r t / f o r m - d a t a ) . Данный элемент удобно ис­
пользовать в составе Web-страниц технической поддержки, где пользователь может
не только описать возникшую проблему, но и приложить к описанию данные, сгене­
рированные системой, или конфигурационные файлы.

Совет

Если форма содержит элемент копирования файлов, в дескрипторе


<FOPM> обязательно должен присутствовать атрибут
ENCTYPE= "mul tlpart/form-da ta "-

NAME
Атрибут NAME идентифицирует поле редактирования, содержащееся в составе
элемента.

VALUE
Из соображений безопасности этот атрибут игнорируется. Имя файла может вве­
сти только пользователь.

SIZE и M A X L E N G T H
Атрибуты SIZE и MAXLENGTH используются точно так же, как и одноименные ат­
рибуты поля редактирования. Атрибут SIZE определяет число видимых символов,
а атрибут MAXLENGTH — максимальную длину строки.

ACCEPT
Значением атрибута ACCEPT является список MIME-типов. С помощью данного
атриб)а^а можно ограничить список дост)^пных имен файлов, однако его поддер­
живают не все броузеры.
806 Глава 18. HTML-формы

DISABLED, READONLY
Эти атриб)а'ы позволяют запретить доступ к элементу или перевести его в режим
только для чтения. Несмотря на то что атрибуты DISABLED и READONLY включены
в состав спецификации HTML 4.0, броузер Netscape 4.x не поддерживает их.

ONCHANGE, ONSELECT, ONFOCUS и ONBLUR


Эти атрибуты используются броузерами, которые поддерживают JavaScript и оп­
ределяют действия, предпринимаемые тогда, когда курсор покидает поле редак­
тирования после того, как его содержимое было изменено, при выборе элемента
пользователем, при получении и потере фокуса ввода.
Приведенный ниже фрагмент кода представляет собой пример использования
элемента копирования файлов. На рис. 18.20 показано окно, которое отображается
после щелчка на кнопке Browse.
<FORM ACTION="http://localhost:8088/SomeProgram"
ENCTYPE="multipart/form-data">
Enter data file below:<BR>
<INPUT TYPE="FILE" NAME="fileName">
</FORM>

Enter datafilebelow:
Browse... Рис. 18.19. Внешний вид элемента
копирования файлов

Lookr*:] _jWINDOVv/S

й bmdlog.Ul .'j^CDPLuYEREXE
£] Bubbles bmp
SiCALCEXE 5? C\^ne\ Screen Sav(
^ cap_pi.ini *VCHARMAPEXE
^ Carved Stone.bmp JC] Circles.bmp
^£pcd32exe ^CLEANMGREXE

Li J 11
I File name: jcdpiaver in, UP&n
Рис. 18.20. Окно выбора файла, которое
I F3es 0} type jAP "^ilei r ' l •ц
отображается при активизации кнопки Browse

18.8. Карты изображений на стороне сервера


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

когда фиксированный набор URL необходимо поставить в соответствие конкретным


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

IMAGE — стандартные карты изображений


на стороне сервера
Чаще всего изображения на стороне сервера создаются посредством включения в
состав формы элемента <INPUT TYPE="IMAGE" . . . >.

Элемент: <INPUT TYPE="IMAGE" ...> (закрывающий дескриптор


отсутствует)
А т р и б у т ы : NAME ( о б я з а т е л ь н ы й ) , SRC, ALIGN, DISABLED
Данный элемент выводит изображение, после щелчка на котором сервлету или
другой обрабатывающей программе на стороне сервера (URL программы задается с
помощью атриб)а'а ACTION) пересылается содержимое формы. Вместо имени эле­
мента на сервер передаются выражения гсмя. х=координаша_х и имя.у=координата_у.
Координаты вычисляются относительно верхнего левого угла изображения.

NAME
Атрибут NAME идентифицирует элемент в составе формы, и его значение переда­
ется на сервер.

SRC
Атрибут SRC определяет URL изображения.

ALIGN
Атрибут ALIGN может принимать те же значения (ТОР, MIDDLE, BOTTOM, LEFT,
RIGHT; по умолчанию— BOTTOM) и используется так же, как и атрибут ALIGN эле­
мента IMG.

DISABLED
Данный атрибут позволяет запретить доступ к карте изображения. Несмотря на то
что атрибут DISABLED предусмотрен спецификацией HTML 4.x, броузер Netscape
4.x не поддерживает его.
В листинге 18.5 представлен код простого примера, демонстрирующего использо­
вание карты изображений. В качестве значения атрибута ACTION формы указан URL
программы EchoServer, рассмотренной в разделе 17.8. На рис. 18.21 показано ис­
ходное состояние док}^мента, а на рис. 18.22 — содержимое окна броузера после щелч­
ка мышью на изображении.
808 Глава 18. HTML-формы

Листинг 1 8 . 5 . ImageMap.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>The IMAGE Input Control</TITLE>
</HEAD>

<BODY>
<H1 ALIGN="CENTER">The IMAGE Input Control</Hl>
Which island is Java? Click and see if you are correct.

<FORM ACTION="http://localhost:8088/GeographyTester">
<INPUT TYPE="IMAGE" NAME="map" SRC="images/indonesia.gif">
</FORM>

Of course, image maps can be implemented <B>in</B>


Java as well. :-)

</BODY>
</HTML>

0t 1<» ^mf f;8vert«« look b*tP к.ж^

The IMAGE Input C^ontrol


Which islarid is Java^ Click and see if you are correct

Of course, unage maps can be implemented ш Java as weD -)


-ll
gj'Oone ^MyCoRwtet

Рис. 18.21. Элемент INPUT типа IMAGE с именем NAME="map"


18.8. К а р т ы и з о б р а ж е н и й на с т о р о н е с е р в е р а 809

!^1fri-frailffMiT^I'IVIIIill>'ii'J4'.!''!l-MHHHHHHHHHHHHH^ I

EchoServ'er Results
Here IS the request line and request headers sent by your browser;

GET /GeographyTester?niap.x-305«itiap.y-280 HTTP/1.1


Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appllcation/wswcrd, application/vnd.ms-excei, application/v
Accept-Language: en-us
Accept-Encoding: gzip, d e f l a t e
User-Agent: H o z i l l a / 4 . 0 (compatible; HSIE 5.0; Windows 98; DigExt)
Host: localhost:80e8
Connection: Keep-Alive

^
g j Done * ^ locd ЫитЫ

Рис. 18.22. После щелчка мышью в точке (305, 280) содержимое формы передается на сервер.
В состав строки параметров включается последовательность символов map.x=305&map.y=280

ISMAP — альтернативные карты изображений


на стороне сервера
ISMAP— это необязательный атрибут дескриптора <IMG>. Элемент IMG с атрибу­
том ISMAP может использоваться для взаимодействия с сервлетами или CGI-npor-
раммами на стороне сервера. Если такой элемент оформлен как гипертекстовая ссыл­
ка, после щелчка на изображении координаты курсора мыши в момент щелчка пере­
даются по указанному URL. Координаты вычисляются относительно верхнего левого
угла изображения и разделяются запятой.
В листинге 18.6 приведен код примера, в котором изображение с атрибутом ISMAP
оформлено в виде гипертекстовой ссылки, указывающей на ресурс h t t p : / /
l o c a l h o s t : 8 0 8 8 / C h i p T e s t e r . По данному адресу расположен мини-сервер H T T P ,
рассмотренный в разделе 17.8. На рис. 18.23 показано изображение, которое не отли­
чается от изображения, созданного с помощью элемента IMG без атрибута ISMAP. Од­
нако, если вы щелкнете мышью в точке, отстоящей от верхнего левого края экрана по
оси X на 271 пиксель и по оси у на 184 пикселя, в составе запроса броузера будет пе­
редан URL h t t p : / / l o c a l h o s t : 8 0 8 8 / C h i p T e s t e r ? 2 7 1 , 184 (рис. 18.24).

Листинг 1 8 . 6 . I s M a p . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>The ISMAP A t t r i b u t e < / T I T L E >
810 Глава 18. HTML-формы

</HEAD>

<BODY>

<Н1 ALIGN="CENTER">The ISMAP Attribute</Hl>


<H2>Select a pin:</H2>
<A H R E F = " h t t p : / / l o c a l h o s t : 8 0 8 8 / C h i p T e s t e r " >
<IMG S R C = " i m a g e s / c h i p . g i f " WIDTH=495 HEIGHT=200 ALT="Chip"
BOiyDER=0 ISMAPX/A>

</BODY>
</HTML>

Mi.!iHi'i!ii.fjiiM",MamMi
fie £,* View go СотгшпкяШ» МФ

4: ->' -л A^W^^'t%'m'
The ISMAP Attribute
Select a pin:

Рис. 18.23. При включении в состав


гипертекстовой ссылки элемента IMG
с атрибутом ISMAP порядок
•^Щ^' ^Wpc/^eoalwtt i^8/ChipTe$«ef1 •^,iS^ m ^,г^ формирования запроса изменяется

^^ -/ 3 fif ^.^ '^ ^;^ rf а :}| Si:


Echo Seiner Results
Here is the request line and request headers sent by your browser:

GET / C h i p T e s t e r ? 2 7 1 , 1 8 4 HTTP/1.0
Connect-ion: Keep-Alive
User-Agent: M o z i l l a / 4 . 7 [en] (¥in98; U)
Host: localhost:8088
Accept: iinage/gif, image/x-xbitinap, image/jpeg, image/pjpeg, imat
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8

Рис. 18.24. При активизации ссылки,


содержащей изображение ISMAP, в
J JL! составе URL передаются координаты
-^-*'-ш- ^ Л- ' 3 -^ ^ курсора мыши
18.9. Скрытые поля 811

Карты изображений на стороне сервера не следует использовать для связывания


фиксированного набора URL с областями в составе изображения. С этой задачей го­
раздо лучше справятся карты изображений на стороне клиента, поскольку в этом слу­
чае для выбора URL не нужно обмениваться данными с сервером. Если карта изобра­
жений должна использоваться вместе с другими интерфейсными элементами в соста­
ве формы, следует отдать предпочтение элементу INPUT типа IMAGE. Если же карта
изображений должна находиться за пределами формы, если URL, связанные с фраг­
ментами изображения, часто изменяются либо если для получения URL требуется
выполнять вычисления, желательно применять элемент IMG с атрибутом ISMAP.

18.9. Скрытые поля


Скрытые поля не влияют на внешний вид док)ТУ[ента. Со скрытым полем связывают­
ся имя и значение, которые не изменяются с момента загрузки документа до момента
передачи содержимого формы на сервер. Назначение скрытых полей описано ниже.
Во-первых, скрытые поля позволяют прослеживать перемещение пользователя в
пределах узла. Следует заметить, что авторы сервлетов обычно применяют средства
API, предназначенные для поддержки состояния сеанса (см. раздел 19.12), и не ис­
пользуют низкоуровневые механизмы хранения информации о сеансе.
Во-вторых, скрытые поля могут использоваться для хранения предопределенных
входных данных, предназначенных для передачи на сервер. В этом случае несколько
различных HTML-документов могут выполнять роль интерфейса к одной программе
на стороне сервера. Предположим, например, что администрация Internet-магазина
выплачивает вознаграждение тем, кто направляет к ним очередного клиента. В этом
случае на Web-странице может содержаться форма с предложениями товаров, а в
скрытом поле — идентификатор страницы, на которой находится эта форма.
В-третьих, скрытые поля применяются для хранения контекстной информации
при динамической генерации HTML-документов. Так, например, документы, созда­
ваемые при взаимодействии с интерактивным магазином, могут хранить в скрытом
поле идентификатор пользователя, информацию о скидках для конкретного потре­
бителя и другие данные.

Элемент: <INPUT TYPE="HIDDEN ' NAME="..." VALUE="...">


(закрывающий дескриптор отсутствует)
Атрибуты: NAME (обязательный), VALUE
С данным элементом связываются имя и значение, однако он не отображается в
окне броузера. Пара имя-значение включается в строку параметров. В следующем
примере при каждой активизации ф о р м ы на сервер будет передаваться пара
itemID=hall001.
<INPUT TYPE="HIDDEN" NA]y[E="itemID" VALUE="hall001">
Заметьте, что термин "скрытый" совсем не означает, что пользователь не может
получить информацию о данном элементе. Несмотря на то что этот элемент не ото­
бражается на экране, работая с броузером, можно открыть страницу с исходным тек­
стом документа и просмотреть HTML-код. Поэтому, разрабатывая HTML-документы,
не следует помещать в скрытые поля пароли и другие конфиденциальные сведения.
812 Глава 1 8 . H T M L - ф о р м ы

18.10. Группировка интерфейсных


элементов
Для объединения интерфейсных элементов, содержащихся в форме, в группы в
спецификации HTML 4.0 предусмотрен элемент FIELDSET и связанный с ним эле­
мент LEGEND. К сожалению, этот полезный элемент поддерживается только Internet
Explorer. Предполагается, что поддержка FIELDSET будет включена в одну из реали­
заций Netscape 6, однако в настоящий момент его можно применять только при соз­
дании документов, предназначенных для сетей intranet, в которых используется ис­
ключительно Internet Explorer.
Внимание!
Netscape 4.7 и более ранние версии данного броузера не поддер­
живают элемент FIELDSET.

Элемент: <FIELDSET> ... </FIELDSET>


Атрибуты: отсутствуют
Элемент FIELDSET выст)^пает в роли контейнера для хранения интерфейсных
элементов; в него также может быть включен элемент LEGEND. Атрибуты, специфиче­
ские для элемента FIELDSET, не предусмотрены. Пример использования данного
элемента приведен в листинге 18.7, а внешний вид документа показан на рис. 18.25.

Листинг 18.7. F i e l d s e t . html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / 7 E N " >


<HTML>
<HEAD>
<TITLE>Grouping C o n t r o l s i n I n t e r n e t E x p I o r e r < / T I T L E >
</HEAD>
<BODY BGC0L0R="#FDF5E6">
<H2 ALIGN="CENTER">Grouping C o n t r o l s i n I n t e r n e t Explorer</H2>
<FORM A C T I O N - " h t t p : / / l o c a l h o s t : 8 0 8 8 / S o m e P r o g r a m " >
<FIELDSET>
<LEGEND>Group One</LEGEND>
F i e l d lA <INPUT TYPE="TEXT" NAME="fieldlA" VALUE="Field A"><BR>
F i e l d IB <INPUT TYPE="TEXT" NAME="fieldlB" VALUE="Field B"><BR>
F i e l d 1С <INPUT TYPE="TEXT" NAME="fieldlC" VALUE="Field C"><BR>
</FIELDSET>
<FIELDSET>
<LEGEND ALIGN="RIGHT">Group Two</LEGEND>
F i e l d 2A <INPUT TYPE="TEXT" NAME="field2A" VALUE="Field A"><BR>
F i e l d 2B <INPUT TYPE="TEXT" NAME="field2B" VALUE="Field B"><BR>
F i e l d 2C <INPUT TYPE="TEXT" NAME="field2C" VALUE="Field C"><BR>
</FIELDSET>
</FORM>
</BODY>
</HTML>
1 8 . 1 1 . Порядок выбора элементов 813

1^ШВШШШВБШЕ |»ипй|||шша
вааг^гяш
- j
Grouping Controls in Internet Explorer
• Group One ' ' ' ; ~- - -
•Field lA. h e l d A
iField IB: JField В
iFieldlCIFieldC
'' Ог01ф Two ;
Field 2A: Held A
Field 2B: jField В
Field 2C [Field С
J Рис. 18.25. FIELDSET позволяет объединять
J:^ Щ СощиЛе!
интерфейсные элементы в группы

Элемент: <LEGEND>... </LEGEND>


Атрибуты: ALIGN
Данный элемент применяется только в составе элемента FIELDSET. Он включает
текстовую метку в состав обрамления, отображаемого вокруг группы элементов.

ALIGN
Атрибут ALIGN управляет расположением текстовой метки. Значениями данного
атрибута могут быть ТОР (по умолчанию), BOTTOM, LEFT и RIGHT. На рис. 18.25 по­
казаны две группы и н т е р ф е й с н ы х элементов. В одной из них принимается распо­
ложение метки по умолчанию, в другой задан атрибут ALIGN="RIGHT". В настоя­
щее время для управления выравниванием рекомендуется использовать листы
стилей. П р и этом внесенные изменения могут распространяться на различные
части документа.

1 8 . 1 1 . Порядок выбора элементов


В спецификации HTML 4.0 описан атрибут TAB INDEX, который может быть ис­
пользован с любым элементом, отображаемым на экране. Значением данного атрибу­
та является целое число, определяющее порядок получения элементом фокуса ввода
при нажатии пользователем клавиши <ТаЬ>. Несмотря на то что данный атрибут под­
держивается только Internet Explorer, вы можете использовать его в документах,
предназначенных для публикации в Internet. Дело в том, что порядок перебора эле­
ментов устанавливается только для удобства пользователя и не может быть источни­
ком ошибок при работе с Web-страницей. П р и м е р применения атрибута TAB INDEX
приведен в листинге 18.8, а внешний вид документа показан на рис. 18.26.

Внимание!

Netscape 4.7 не поддерживает атрибут TABINDEX.


814 Глава 18. HTML-формы

Листинг 18.8. Tabindex.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Controlling TAB Order</TITLE>
</HEAD>
<BODY BGC0L0R="#FDF5E6">
<H2 ALIGN="CENTER">Controlling TAB Order</H2>
<FORM ACTION="http://localhost:8088/SomeProgram">
Field 1 (first tab selection):
<INPUT TYPE="TEXT" NAME="fieldl" TABINDEX=1><BR>
Field 2 (third tab selection):
<INPUT TYPE="TEXT" NAME="field2" TABINDEX=3><BR>
Field 3 (second tab selection):
<INPUT TYPE="TEXT" NAME="fieldS" TABINDEX=2><BR>
</FORM>
</BODY>
</HTML>

Рис. 18.26. В Internet Explorer при последова­


"U тельных нажатиях клавиши <Tab> фокус ввода
Controlling TAB Order получает сначала первое, затем третье, а потом
Field 1 (first tab selection): j второе поле редактирования (порядок перебора
Field 2 (third tab selection): Г " элементов определен с помощью атрибута
Field 3 (second tab selection); j TABINDEX). В Netscape фокус ввода передается
полям редактирования в том порядке, в каком они
\^Hf6»ntij^^ были включены в форму

18.12. Резюме
HTML-формы создаются на базе элемента FORM, в состав которого включаются
элементы INPUT, предназначенные для получения данных. Атрибут ACTION дескрип­
тора <FORM> задает URL программы на стороне сервера, предназначенной для обра­
ботки данных. С каждым из элементов INPUT связывает имя и значение. Имя указыва­
ется в составе HTML-документа, а значение либо задается с помощью HTML-кода, ли­
бо формируется в результате действий пользователя. Пары имя-значение передаются
серверу. При этом они либо присоединяются к URL (в случае запроса GET), либо по­
мещаются в отдельную строку (в случае запроса POST).
В этой главе мы рассмотрели вопросы сбора данных. Однако основную работу по
решению задачи, поставленной перед системой, выполняет программа обработки.
Обсуждению обрабатывающих программ на стороне сервера посвящены две следую­
щие главы.
JAVA НА СТОРОНЕ
СЕРВЕРА: СЕРВЛЕТЫ

В ЭТОЙ главе...

Преимущества сервлетов по сравнению с другими


технологиями.

Серверы, поддерживающие сервлеты и JSP.

Структура и жизненный цикл сервлета.

Инициализированные параметры сервлета.

Доступ к данным формы.

Поля заголовка и коды состояния, соответствующие


протоколу HTTP 1.1.

Средства, используемые сервлетами вместо


CGI-переменных.

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

Использование cookie при работе сервлетов.

Поддержка сеанса.
J~y\ZJ^3J

С ервлеты — это альтернатива CGI-программам (Common Gateway Interface — ин­


т е р ф е й с общего шлюза). Сервлеты выполняются на Web-сервере и играют роль
серверов промежуточного уровня. О н и получают запросы от W^eb-броузеров и
других HTTP-клиентов и выполняют необходимые операции с базами данных и при­
ложениями, выполняемыми на стороне сервера. В процессе работы сервлет выпол­
няет следующие действия.
1. Чтение данных, переданных пользователем.
Эти данные чаще всего передаются формами, содержащимися в составе Web-
страниц, но могут также приходить от аплетов и специальных HTTP-клиентов.
2. Получение прочей информации, переданной в HTTP-запросе.
В составе запроса передаются сведения о возможностях броузера, записях
cookie, имени узла, на котором выполняется клиент, и т.д.
3. Генерация результатов.
Процесс генерации результатов может включать взаимодействие с базой данных,
вызовы методов средствами RMI или CORBA, обращение к приложениям и т. д.
4. Форматирование результатов обработки.
В большинстве случаев форматирование предполагает о ф о р м л е н и е данных в
виде HTML-документа.
5. Установка параметров HTTP-ответа.
В составе ответа броузер получает информацию о типе возвращаемого доку­
мента (например, HTML), записи cookie, данные, запрещающие или разре­
шающие кэширование, и другие сведения.
6. Передача документа клиенту.
Документ может быть передан в текстовом формате (HTML), в виде двоичного
файла (GIF-изображения) и даже в сжатом виде (gzip-файл).
818 Глава 19. Java на стороне сервера: сервлеты

В большинстве случаев запросы клиентов предполагают передачу готовых доку­


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

• Если Web-страница должна создаваться на основе данных, переданных


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

1 9 . 1 . Преимущества сервлетов перед


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

Эффективность
в традиционных CGI-программах для обработки каждого запроса запускается новый
процесс. Если объем вычислений, выполняемый программой, невелик, то время, необ­
ходимое для выполнения вспомогательных операций, связанных с запуском и поддерж­
кой нового процесса, мог)^' превышать время работы самой программы. При использо­
вании сервлетов виртуальная машина Java запускает каждый сервлет в виде "легковес­
ного" потока. Для традиционных CGI-программ при одновременном получении N за­
просов в память загр)окается N копий программы. Для сервлетов в этом сл)^ае органи-
1 9 . 1 . Преимущества сервлетов... 819

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

Простота использования
Сервлеты предоставляют мощные средства, позволяющие выполнять автоматиче­
ский разбор и декодирование данных, передаваемых HTML-формами, обрабатывать
cookie, поддерживать состояние сеанса и выполнять другие действия, связанные с об­
служиванием запросов. Кроме того, если разработчик знает Java, зачем ему изучать
также язык Perl? Если вы согласны, что Java-технология позволяет создавать более на­
дежный код, допускающий повторное использование, стоит ли возвращаться к C++
для написания программ на стороне сервера?

Богатые возможности
Сервлеты предоставляют ряд возможностей, которые трудно, а иногда и невоз­
можно реализовать в обычных CGI-программах. Так, например, сервлеты могут непо­
средственно взаимодействовать с Web-сервером. Для CGI-программ это возможно
только при условии использования API сервера. Взаимодействие с Web-сервером уп­
рощает преобразование относительных URL в абсолютные и выполнение других за­
дач, связанных с обработкой заголовка запроса. Различные сервлеты могут использо­
вать общий набор данных, что позволяет организовать пул соединений с базой. Серв­
леты могут хранить данные между запросами, что упрощает поддержку сеанса и дает
возможность обеспечить кэширование результатов предыдущих вычислений.

Переносимость
Сервлеты реализуются на языке Java и используют стандартные наборы APL По­
этому сервлет, написанный, например, для работы с iPlanet Enterprise Sei^ver, может
практически без изменений использоваться совместно с Apache, Microsoft Internet In­
formation Server (IIS), IBM WebSphere, StarNine WebStar и другими серверами. Реаль­
но сервлеты могут работать практически с каждым из современных Web-серверов (в
некоторых продуктах для этой цели необходимо использовать дополнительные моду­
ли). Набор средств для работы с сервлетами включен в состав Java 2 Platform, Enter­
prise Edition (J2EE; см. h t t p : / / j a v a . s u n . c o m / j 2 e e / ) , благодаря чему задача под­
держки сервлетов в корпоративных средах существенно упрощается.

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

бенно тщательно подходить к обработке символов, которые могут быть специальным


образом интерпретированы оболочкой (кавычки, точка с запятой и т.д.) Эта пробле­
ма серьезнее, чем может показаться на первый взгляд, и даже в широко распростра­
ненных библиотеках CGI-программ постоянно обнаруживаются ошибки, которые мо­
гут влиять на качество защиты.
Еще одним источником проблем при работе CGI-программ является тот факт, что
языки, на которых эти программы реализованы, не выполняют автоматическую про­
верку на переполнение массивов (в частности, строк символов). Например, в С и C++
можно записать тысячу символов в 100-элементный массив. Очевидно, что 900 знаков
будут помещены за пределы массива. В области памяти, где будут размещены эти зна­
ки, находится также и исполняемый код. Злоумышленник может специальным обра­
зом подобрать набор символов в строке и изменить код программы так, чтобы про­
грамма выполняла необходимые ему действия. На этом эффекте построены атаки с
переполнением буфера. П р и работе с сервлетами подобные проблемы не могут воз­
никнуть. Если даже сервлет обрабатывает удаленный запрос, связанный с вызовом
программы в локальной операционной системе, он не использует при этом оболочку.
И, конечно же, проверка границ массивов и другие средства защиты памяти являются
основными элементами языка Java.

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

19.2. Инсталляция и настройка сервера


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

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


сервлетов и JSP
Перед написанием первого сервлета необходимо найти и скопировать програм­
мы, реализующие спецификации Java Servlet 2.1 или 2.2 nJavaServer Pages 1.0 или 1.1.
Если у вас уже установлен современный Web-сервер или сервер приложений, про­
верьте, обладает ли он указанными возможностями. Для этого надо обратиться к до-
19.2. Инсталляция и настройка сервера 821

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


находится по адресу h t t p : / / j a v a . s u n . c o m / p r o d u c t s / s e r v l e t / i n d u s t r y . h t m l .
Для организации работы профессионального Web-сервера вам, вероятно, потребует­
ся коммерческий продукт, однако полезно также установить на персональном компь­
ютере одну из свободно распространяемых программ поддержки сервлетов. Она при­
годится для разработки и тестирования небольших программ, предназначенных для
выполнения на стороне сервера. Некоторые из популярных продуктов, обеспечи­
вающих работу сервлетов, перечислены ниже.

• Apache Tomcat
Пакет Tomcat реализует спецификацию Java Sei-vlet 2.2 и может либо использо­
ваться как независимый сервер для тестирования сервлетов, либо быть интег­
рирован в состав сервера Apache. В настоящее время существуют версии данно­
го продукта практически для любой операционной системы, поддерживающей
платформу Java 2. Подробную информацию об Apache Tomcat вы найдете по
адресу h t t p : / / J a k a r t a . a p a c h e . o r g / t o m c a t / .
• Allaire JRun
J R u n представляет собой продукт с поддержкой Java Sei-vlet 2.2 и JSP 1.1,
встраиваемый в состав серверов Netscape Enterprise или FastTrack, IIS, Micro­
soft Personal Web Server, Apache, O'Reilly WebSite и StarNine WebStar. Он хоро­
шо подходит для разработки программ, но не годится для их доставки. Под­
робную и н ф о р м а ц и ю об Allaire J R u n вы найдете по адресу h t t p : / / w w w .
a l l a i r e . com/products/j run/.
• ServletExec
ServletExec поддерживает спецификации Java SeiMet 2.2 и JSP 1.1 и может
встраиваться в популярные Web-серверы, предназначенные для работы в сис­
темах Solaris, Windows, MacOS, HP-UX и Linux. Д а н н ы й продукт можно скопи­
ровать и использовать бесплатно, однако для работы некоторыми дополни­
тельными средствами и утилитами администрирования необходимо приобре­
сти лицензию. Подробная информация находится по адресу h t t p : / / w w w .
servletexec.com/.
• LiteWebServer
Продукт LiteWebSei~ver (LWS) представляет собой свободно распространяемый
Web-сервер, разработанный Gefion Software на базе Tomcat. Он поддерживает
Java Sei~vlet 2.2 и JSP 1.1. Gefion Software также предлагает дополнительный
модуль под названием WAICooIRunner, который реализует возможности Servlet
2.2 и JSP 1.1 на серверах Netscape FastTrack и Enterprise. Дополнительная ин­
формация находится по адресу h t t p : //www. g e f i o n s o f t w a r e . com/.
• Caucho Resin
Resin представляет собой средство поддержки сервлетов и JSP, соответствую­
щее спецификации Java SeiMet 2.2 и JSP 1.1. Этот продукт также обеспечивает
уравновешивание нагрузки. Caucho Resin можно бесплатно использовать для
разработки и некоммерческой доставки программ. И н ф о р м а ц и я о данном про­
дукте находится по адресу h t t p : //www. c a u c h o . c o m / p r o d u c t s / r e s i n / .
822 Глава 19. Java на стороне сервера: сервлеты

• JavaServer Web Development Kit


JavaServer Web Development Kit QSWDK) — официальная реализация специфи­
каций Java Servlet 2.2 и JSP 1.1. Данный продукт может быть использован как не­
зависимый сервер для тестирования сервлетов и JSP. Его описание находится
по а д р е с у h t t p : / / J a v a . s u n . c o m / p r o d u c t s / s e r v l e t / d o w n l o a d . h t m l .

Документация, необходимая для разработки


сервлетов и JSP
Как вы уже знаете, ни один серьезный программист не приступает к разработке Java-
программ, не имея доступа к документации n a J D K 1.1 или 1.3 API. Точно так же, чтобы
начинать работу над сервлетами, надо иметь под рукой описание классов, содержащих­
ся в пакете j a v a x . s e r v l e t . Ссылки на дoк)^v^eнтaцию по API приведены ниже.

• http://java.sun.com/products/jsp/download.htinl
По данному адресу можно найти и скопировать на локальный компьютер доку­
ментацию на 2.1/1.0 API или 2 . 2 / 1 . 1 API. П р и этом необходимо скопировать
весь пакет, а затем извлечь из него требуемые документы.
• http://java.sun.eom/products/servlet/2.2/javadoc/
На этом узле вы можете просматривать в интерактивном режиме документа­
цию на Java Servlet 2.2 и JSP 1.1.
• http://www.java.sun.com/j2ee/j2sdkee/techdocs/api/
По данному адресу находится полный набор документов, 1!чи._;мвс-::"Лц;1А /J. i Java 2
Platform, Enterprise Edition (J2EE), включающий пакеты Servlet 2.2 и JSP 1.1.

Информация о расположении файлов классов


Имея необходимое программное обеспечение, вы должны сообщить компилятору
Java о том, где следует искать файлы классов для сервлетов и JSP. Подробную инфор­
мацию вы найдете в документации на свою систему. Здесь же мы можем лишь сказать,
что обычно файлы классов расположены в подкаталоге l i b каталога, в котором уста­
новлен сервер; классы сервлетов находятся в архиве s e r v l e t . j a r , а файлы JSP— в
j s p . j a r , j s p e n g i n e . j a r или j a s p e r , j a r . Проще всего сообщить j a v a c об этих
классах, включив ссылки на них в переменную окружения CLASS PATH. Если
CLASS PATH не используется, компилятор ищет необходимые файлы в текущем ката­
логе и в стандартных системных библиотеках. Задавая значение переменной
CLASSPATH, обязательно включите ссылку на текущий каталог ("."). Подробно о ра­
боте cJAR-архивами рассказано в разделе 7.10.

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


Чтобы устранить конфликты с сервлетами, которые были созданы другими разра­
ботчиками и размещены на том же Web-сервере, вы, наверное, захотите поместить
ваши сервлеты в отдельные пакеты. Возможно, вам придется добавить к переменной
окружения CLASSPATH ссылки на каталоги верхнего уровня в иерархии пакетов.
19.2. Инсталляция и настройка сервера 823

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

Установка памяти DOS


Если вы собираетесь запустить сервер Tomcat или JSWDK в системе Windows 95
или 98, вам придется изменить объем памяти DOS, выделенный для переменных ок­
ружения. Для этого откройте окно DOS, щелкните на пиктограмме MS-DOS в левом
верхнем углу окна и выберите пункт Properties (Свойства). В этом окне откройте
вкладку Memory (Память) и измените значение опции Initial Environment (Переменные
среды) с Auto на 2816. Эти действия необходимо выполнить лишь один раз.

Компиляция и установка сервлетов


Если вы корректно задали значение переменной окружения CLASS PATH, то сможете
выполнять компиляцию сервлета по команде j a v a c S e r v l e t N a m e . J a v a (где
S e r v l e t N a m e — выбранное вами имя сервлета). Полученные файлы классов надо по­
местить в тот каталог, где сервер будет искать их в процессе работы. Как вы, вероятно,
догадались, расположение файлов классов различается для разных серверов. Конкрет­
ные сведения вы найдете в документации на ваш сервер. Ниже приведены данные для
серверов Tomcat и JSWDK. В обоих случаях считается, что i n s t a l l d i r — это главный
каталог, в котором инсталлирован сервер.

Tomcar 3
• instalLdir/webapps/ROOT/WEB-INF/classes
Стандартное расположение классов сервлетов. Все серверы, поддерживающие
Servlet 2.2 и JSP 1.1, содержат инсталляционный каталог, путь к которому выгля­
дит приблизительно так: . . . / W E B - I N F / c l a s s e s . Например, для Allaire JRun он
имеет вид i n s t a l l _ d i r / s e r v e r s / d e f a u l t / d e f a u l t - a p p / W E B - I N F / c l a s s e s .
• install_dir/lib
Расположение JAR-файлOB с кодами классов.
• instalLdir/webapps/ROOT
Расположение HTML-файлов, изображений и JSP. Каталог подобного назначения
также существует во всех серверах с поддержкой Sei-vlet 2.2 и JSP 1.1. Например,
Allaire J R u n использует для этой цели каталог i n s t a l l d i г / s e r v e r s /
default/default-app/.

JSWDK 1.0.1
• install_dir/webpages/WEB-INF/servlets
Стандартное расположение классов сервлетов.
824 Глава 19. Java на стороне сервера: сервлеты

• install_dir/lib
Расположение JAR-файлов с кодами классов,
• install_dir/webpages
Расположение HTML-файлов, изображений и JSP.

Обращения к сервлетам
Большинство серверов позволяет регистрировать имена сервлетов, поэтому обра­
щаться к ним можно в форме h t t p : //узел/путь/имя. Конкретные особенности вызова
сервлетов подробно описаны в документации на сервер. Сутцествует универсальный
способ обращения к сервлетам, при котором URL сервлета задается в виде
\\Х,Х,^\ //узел/s^TV^Q'L/имя_сервлвта. Обратите внимание, что в данном URL применя­
ется имя s e r v l e t (без буквы " s " в конце), несмотря на то, что реальный каталог обыч­
но называется s e r v l e t s и даже может иметь другое имя, например c l a s s e s или l i b .
В настоящее время по умолчанию используются два варианта URL. В первом из
них в состав URL входит имя пакета, в котором находится сервлет. Сервлеты поме­
щаются в пакеты для того, чтобы избежать возникновения конфликтов имен. В этом
случае URL имеет вид htt'p: //узел/servlet/имя_пакета.имя_сервлета. Многие со­
временные Web-серверы позволяют определять Web-приложения — наборы сервле­
тов и JSP (а также изображений, HTML-документов и т.д.). В этом случае URL, ис­
пользуемый для обращения к сервлету, выглядит так: htt'p: //узел/пуmb_K_Web-
приложению/servlet/имя_пакета.имя_сервлета. Кроме того, большинство Web-
серверов позволяет определять произвольные отображения URL в сервлеты, незави­
симо от того, используются ли пакеты и Web-приложения.

19.3. Базовая структура сервлета


В листинге 19.1 приведен код простейшего сервлета, обрабатывающего запрос
GET. Для тех, кто незнаком с протоколом HTTP, заметим, что GET— это наиболее
часто встречающийся тип HTTP-запроса. Броузер генерирует этот запрос в том слу­
чае, когда пользователь вводит URL в строке адреса либо активизирует гипертексто­
вую ссылку на Web-странице. Кроме того, запрос GET передается при активизации
формы, для которой в дескрипторе <Г0К]У1> указано значение GET атрибута METHOD.
Сервлет также может обрабатывать запрос POST, который генерируется в том случае,
когда в дескрипторе <FORM> задан атрибут METHOD="POST". Подробно вопросы ис­
пользования HTML-форм были рассмотрены в главе 18.

Листинг 1 9 . 1 . S e r v l e t T e m p l a t e . J a v a

import java.io.*;
import javax.servlet.^;
import javax.servlet.http.*;
public class ServletTemplate extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
19.3. Базовая структура сервлета 825

throws ServletException, lOException {

/ / Параметр r e q u e s t и с п о л ь з у е т с я для ч т е н и я полей


/ / з а г о л о в к а , содержащихся в H T T P - з а п р о с е , и данных,
/ / введенных п о л ь з о в а т е л е м п о с р е д с т в о м HTML-формы.

/ / Параметр r e s p o n s e " и с п о л ь з у е т с я для у с т а н о в к и кода


/ / с о с т о я н и я HTTP-ответа и полей з а г о л о в к а .

P r i n t W r i t e r out = r e s p o n s e . g e t W r i t e r ( ) ;
/ / Объект o u t и с п о л ь з у е т с я для п е р е д а ч и данных б р о у з е р у .

Класс, реализующий сервлет, должен являться подклассом класса H t t p S e r v l e t .


В нем переопределяется метод d o G e t или d o P o s t . При обработке запроса GET вызы­
вается метод d o G e t , а при обработке запроса POST — d o P o s t . Если в сервлете при по­
лучении любого типа запроса должны выполняться одинаковые действия, целесооб­
разно написать программу так, чтобы в теле метода d o G e t вызывался метод d o P o s t ,
либо, наоборот, чтобы d o P o s t обращался к d o G e t .
При вызове каждого из указанных методов ему передаются два параметра, в рол^
которых выступают экземпляры H t t p S e r v l e t R e q u e s t и H t t p S e r v l e t R e s p o n s e .
H t t p S e r v l e t R e q u e s t предоставляет методы, с помощью которых можно получать
различную информацию о запросе, например содержимое полей заголовка, данные,
передаваемые посредством формы, адрес клиентского узла и т.д. H t t p S e r v l e t ­
R e s p o n s e позволяет формировать ответ клиенту. Ответ содержаит код состояния
(200, 404 и др.) и набор полей заголовка ( C o n t e n t - T y p e , S e t - C o o k i e , и т.д.) Кроме
того, с помощью H t t p S e r v l e t R e s p o n s e можно создать объект P r i n t W r i t e r , кото­
рый дает возможность организовать передачу документа клиенту. В простых сервле-
тах генерация выходных данных осуществляется посредством метода p r i n t l n . Дан­
ные, передаваемые с помощью формы, HTTP-запросы, HTTP-ответы и записи cookie
будут рассмотрены в последующих разделах.
Поскольку при выполнении d o G e t и d o P o s t могут возникнуть два исключения, их
необходимо указать при переопределении методов. Кроме того, создавая аплет, надо
импортировать пакеты j a v a . i o (классы P r i n t W r i t e r и др.), j a v a x . s e r v l e t
(классы H t t p S e r v l e t и др.) и j a v a x . s e r v l e t . h t t p (классы H t t p S e r v l e t R e q u e s t
и HttpServletResponse).

Сервлет, генерирующий текстовое сообщение


в листинге 19.2 представлен простой сервлет, который выводит строку текста.
Внешний вид окна броузера после выполнения сервлета показан на рис. 19.1. Сервлет,
который оформляет ту же строку в виде заголовка HTML-документа, представлен в лис­
тинге 19.3. Перед тем как рассматривать сервлеты, генерирующие HTML-документы,
необходимо сказать несколько слов об инсталляции, компиляции и запуске простого
сервлета.
Во-первых, необходимо убедиться в том, что настройка сервера выполнена кор­
ректно и что переменная окружения CLASS PATH ссылается HaJAR-файл, содержащий
826 Глава 19. Java на стороне сервера: сервлеты

пакет j a v a x . s e r v l e t (см. раздел 19.2). Во-вторых, необходимо выполнить команду


j a v a c HelloWorld. Java (либо скомпилировать исходный код в интегрированной
среде разработки). В-третьих, следует скопировать файл HelloWorld. c l a s s в ката­
лог, используемый сервером для хранения сервлетов (это может быть каталог
i n s t a l l _ d i r / . . ./WEB-INF/classes). Для того чтобы поместить файлы классов
Java в требуемый каталог, можно использовать опцию -D утилиты j a v a c . Наконец,
для вызова сервлета надо обратиться к нему по адресу h t t p : / / у з е л / s e r v l e t /
и м я с е р влета либо использовать URL, специфический для конкретного броузера.
Сервлет, выходные данные которого показаны на рис. 19.1, был запущен путем обра­
щения в серверу, выполняющемуся на локальной машине.

Листинг 19.2.HelloWorld.Java

import j a v a . i o . * ;
import j a v a x . s e r v l e t . * ;
import j a v a x . s e r v l e t . h t t p . * ;
p u b l i c c l a s s HelloWorld extends H t t p S e r v l e t {
p u b l i c void doGet(HttpServletRequest r e q u e s t ,
HttpServletResponse response)
throws S e r v l e t E x c e p t i o n , lOException {
P r i n t W r i t e r out = r e s p o n s e . g e t W r i t e r ( ) ;
o u t . p r i n t l n C ' H e l l o World");
}

Fie £ Л ytew go

rri ^.. a> --di a a :ll Я


^ ljlxafoft-|httpMocalhost/se>vlet/HelloWofld^^^'^^

Hello World Рис. 19.1. Результаты выполнения кода,


представленного в листинге 19.2
Ooain ^ \^ J..? : i | . ^ ,,\ (HelloWorld.j ava)

Сервлет, генерирующий HTML-KOM


Большинство сервлетов в процессе работы генерирует HTML-код. Для этого серв­
лет выполняет следующие действия.
1. Сообщает броузеру о том, что передаваемые данные представлены в формате
HTML
2. Включает HTML-дескрипторы в строки, предназначенные для метода p r i n t In.
Для выполнения первого из указанных действий в состав HTTP-ответа помещает­
ся поле заголовка Content-Туре. Для включения полей заголовка служит метод
s e t H e a d e r объекта H t t p S e r v l e t R e s p o n s e , однако Content-Type используется
настолько часто, что в классе H t t p S e r v l e t R e s p o n s e был предусмотрен специаль­
ный метод s e t C o n t e n t T y p e . Формат HTML описывает М1МЕ-тип t e x t / h t m l , по­
этому вызов метода выглядит следующим образом:
response.setContentType("text/html");
19.3. Базовая структура сервлета 827

Помимо HTML, сервлеты могут передавать клиенту и другие типы данных. Так,
например, нередко в результате выполнения сервлета генерируется изображение в
формате GIF ( i m a g e / g i f ) или таблица Excel ( a p p l i c a t i o n / v n d . m s - e x c e l ) .
Подробно структура HTTP-ответа будет рассмотрена в разделе 19.10. Заметьте, что
заголовки ответа необходимо устанавливать перед тем, как записывать содержимое
ответа, используя для этого объект P r i n t W r i t e r . Дело в том, что HTTP-ответ состо­
ит из строки состояния, одного или нескольких полей заголовков, после которых
следует пустая строка и документ, передаваемый броузеру. Порядок включения полей
заголовков не имеет значения; более того, строка состояния может быть сформиро­
вана после всех полей. С документом дело обстоит по-другому. Содержимое документа
не всегда сохраняется в буфере, поскольку пользователю удобнее работать, если он
может просматривать начало Web-страницы в то время, как остальная часть докумен­
та продолжает загружаться. В версии 2.1 спецификации сервлетов буферизация вы­
вода посредством объекта P r i n t W r i t e r не была предусмотрена, и когда начиналась
запись документа в поток, менять состав заголовка было уже поздно. В версии 2.2 бы­
ла введена частичная буферизация вывода, однако размеры буфера не были оговоре­
ны. Для определения размера буфера используется метод getBuf f e r S i z e объекта
H t t p S e r v l e t R e s p o n s e , а установить размеры буфера можно, вызвав метод
setBuf f e r S i z e . В версии 2.2 есть возможность изменить поля заголовка после нача­
ла вывода документа, однако сделать это можно только в том случае, если буфер еще
не заполнился и реальная передача данных клиенту еще не началась. Если вы не уве­
рены, началась ли передача данных, выяснить это можно с помощью метода
isCoiranitted.
Методика профессионалов

Устанавливать значение поля Content-Type следует перед началом


вывода документа.
%
Включая HTML-дескрипторы в строки, передаваемые методу p r i n t In, надо учи­
тывать структуру HTML-документа, описанную в части I данной книги. В листинге
19.3 приведен пример сервлета, помещенного в пакет cwp. Результаты его выполне­
ния показаны на рис. 19.2. Помните, что при работе с пакетами надо помещать код
сервлета в каталог, соответствующий имени пакета, а переменная окружения
CLASS PATH должна ссылаться на каталог верхнего уровня, содержащий иерархию па­
кетов (в данном примере вся иерархия пакетов состоит из одного каталога cwp).

package cwp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWWW extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
828 Глава 19. Java на стороне сервера: сервлеты

response.setContentType("text/html");
PrintWriter out = response.getWriter();
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n";
out.println(docType +
"<HTML>\n" +
"<HEAD><TITLE>Hello WWW</TITLE></HEAD>\n" +
"<BODY>\n" +
"<Hl>Hello WWW</Hl>\n" +
"</BODY></HTML>");

11 III ll'illll"illilllill'l|i|ll'l I I I I M l l |H|

^ -^i J UJJ ^^ ^Л^ J


AS^es*[#] http/'/locaihost/servlet.^cwpHelloWWW 3 i*'^*^

J
Hello WWW
d Рис. 19.2. Результаты выполнения сервлета,
ёЗ Done |^tocd»i*«ne( •y:^: представленного в листинге 19.3 (HelloWWW. j ava)

Простые утилиты для создания HTML-кода


Как было сказано в части I, HTML-документ имеет следующую структуру:

<!DOCTYPE ...>
<HTML>
<HEAD><TITLE>...</TITLE>...</HEAD>
<BODY ...>...</BODY>
</HTML>

П р и создании HTML-кода посредством сервлета возникает соблазн отказаться от


некоторых элементов, в частности от строки DOCTYPE. Несмотря на то что данная дек­
ларация предусмотрена спецификациями HTML 3.2 и 4.0, подавляющее большинство
броузеров не обрабатывает ее. Мы настоятельно рекомендуем не поддаваться такому
соблазну. Декларация DOCTYPE сообщает программе проверки HTML-кода о том, какой
версии языка должен соответствовать данный документ. Программы проверки сущест­
венно упрощают процесс отладки, сообщая о найденных ошибках в структуре докумен­
та. Эти ошибки могут никак не появляться в одном броузере, но создавать существенные
проблемы с воспроизведением документа другими клиент-программами. Два наиболее
популярных инструмента проверки HTML-кода расположены по адресам h t t p : / /
v a l i d a t o r . w 3 . o r g / и h t t p : / / w w w . h t m l h e l p . c o m / t o o l s / v a l i d a t o r / . Программы
проверки используют URL для обращения к Web-страницам. Поскольку сервлет генери­
рует обычный HTML-документ, он может быть проверен, как и обычная Web-страница.
Программа проверки обращается по }т<азанным URL, используя запрос GET, поэтому в
составе сервлета следует реализовать поддержку данного типа запроса.
1 9 . 3 . Базовая структура сервлета 829

М е т о д и к а профессионалов

Используйте программы проверки ]А/еЬ'Страниц для тестирования


HTML'документов, генерируемых сервлетами.

Генерация HTML-документа посредством вызовов p r i n t I n представляет собой ру­


тинную операцию. В особенности это относится к длинным строкам, каковой является
декларация DOCTYPE. Некоторые разработчики разрабатывают специальные инстру­
менты для создания HTML-кода и используют их в своих сервлетах. Мы готовы поста­
вить под сомнение ценность подобных утилит, достаточно полный набор которых со­
ставил бы библиотеку большого объема. Во-первых, все проблемы, связанные с генера­
цией HTML-текста сервлетами, раз и навсегда решены с помощью технологии JSP,
которая будет рассматриваться в следующей главе. Во-вторых, средства автоматической
генерации HTML-кода явно не поддерживают полный набор HTML-атрибутов
(например, CLASS и ID для листов стилей, атрибуты указания JavaScript-обработчиков,
цвет фона ячеек таблиц и т.д.). Несмотря на недостатки, которыми не могут не обладать
большие библиотеки, предназначенные для создания HTML-кода, целесообразно все-
таки, создать простые классы, с помощью которого можно было бы генерировать наи­
более часто встречающиеся фрагменты HTML-документов. Для стандартных сервлетов
такими фрагментами могут быть декларация DOCTYPE и заголовок HEAD, которые долж­
ны быть включены в состав любого документа и в ближайшем буд)тцем вряд ли претер­
пят существенные изменения. Код, предназначенный для генерации DOCTYPE и HEAD,
приведен в листинге 19.4, а в листинге 19.5 представлен модифицированный вариант
сервлета HelloWWW, использующий методы вспомогательного класса. Еще несколько
подобных инструментов мы предложим далее в этой главе.

Листинг 1 9 , 4 . S e i r v l e t U t i l i t i e s . J a v a

p a c k a g e cwp;
/*^ Простые средства для создания HTML-кода.
* Подобные методы обычно объявляются как static.
V
public class ServletUtilities {
public static final String DOCTYPE =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">";
public static String headWithTitle(String title) {
return(DOCTYPE + "\n" +
"<HTML>\n" +
"<HEAD><TITLE>" + title + "</TITLE></HEAD>\n");
}
// Остальная часть класса ServletUtilities не приводится.
830 Глава 19. Java на стороне сервера: сервлеты

Листинг 1 9 . 5 . SimplerHelloWWW. Java

p a c k a g e cwp;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Простой сервлет, генерирующий HTML-код. Данная версия


* HelloWWW использует методы класса ServletUtilities
* для генерации элементов DOCTYPE, HEAD и TITLE.
Ч
public class SimplerHelloWWW extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.printIn(ServletUtilities.headWithTitle("Hello WWW") +
"<BODY>\n" +
"<Hl>Hello WWW</Hl>\n" +
"</BODY></HTML>");
}
}

19.4. Жизненный цикл сервлета


Выше в этой главе было сказано, что для обслуживания нескольких запросов клиен­
тов создается только одна копия сервлета. Для каждого запроса формируется новый по­
ток, в котором выполняется метод d o G e t или d o P o s t . Сейчас мы рассмотрим более
подробно процесс создания и удаления сервлета, а также вызов различных его методов.
П р и создании сервлета вызывается метод i n i t . В теле этого метода выполняются
действия по инициализации и настройке сервлета. После этого каждое обращение
клиента приводит к созданию потока, в котором вызывается метод s e r v i c e сервлета.
П р и одновременном получении нескольких запросов создается несколько одновре­
менно выполняющихся потоков. Подобное поведение сервлета можно изменить. Ес­
ли сервлет реализует интерфейс S i n g l e T h r e a d M o d e l , то в каждый момент времени
может выполняться только один поток. В теле метода s e r v i c e вызываются d o G e t ,
d o P o s t , а также другие методы doXxx, предназначенные для обработки других типов
запросов клиента. Если по каким-либо причинам сервер примет решение удалить
сервлет из памяти, он вызывает метод d e s t r o y сервлета.

Метод init
Метод i n i t вызывается только при создании сервлета. Каждое последующее об­
ращение клиента не приводит к вызову данного метода. Таким образом, процесс ини­
циализации сервлета можно сравнить с инициализацией аплета. В зависимости от
19.4. Жизненный цикл сервлета 831

способа регистрации, сервлет создается либо при первом обращении клиента по со­
ответствующему URL, либо при запуске сервера. Если сервлет не зарегистрирован, а
лишь помещен в специальный каталог, он создается по обращению пользователя.
Подробно каталоги, предназначенные для размещения сервлетов, были рассмотрены
в разделе 19.2.
Определение метода i n i t сервлета выглядит приблизительно так:
p u b l i c void i n i t O throws S e r v l e t E x c e p t i o n {
/ / Действия по инициализации . . .
}

Чаще всего в теле метода i n i t осуществляется чтение инициализационных пара­


метров, специфических для конкретного сервера. Например, при работе сервлета
могут потребоваться сведения об установках базы данных, о файле паролей, о средст­
вах сериализации и т.д. Чтобы выполнить эту задачу, надо с помощью метода
g e t S e r v l e t C o n f i g получить объект S e r v l e t C o n f i g , а затем вызвать метод
g e t l n i t P a r a m e t e r этого объекта. Пример кода, выполняющего описанные дейст­
вия, приведен ниже.
p u b l i c void i n i t O throws S e r v l e t E x c e p t i o n {
ServletConfig config = g e t S e r v l e t C o n f i g ( ) ;
S t r i n g paraml = c o n f i g . g e t l n i t P a r a m e t e r ( " S o m e P a r a m e t e r " ) ;
}

Значение, передаваемое методу g e t l n i t P a r a m e t e r (имя параметра), и возвра­


щаемая величина (значение параметра) представляют собой объекты S t r i n g . При­
мер использования инициализированных параметров приведен в листинге 19.5.
Несмотря на то что для получения значений параметров используются универ­
сальные средства, процедура их установки зависит от используемого сервера. Так, на­
пример, при работе с Tomcat и сервлетами, использующими средства версии 2.2 спе­
цификации сервлетов, значения свойств помещаются в файл w e b . x m l . При работе с
JSWDK используется s e r v l e t s . p r o p e r t i e s , а для Java Web Server свойства устанав­
ливаются в интерактивном режиме с помощью консоли администрирования.

Метод service
Каждый раз, когда сервер принимает запрос, предназначенный для сервлета, он по­
рождает новый поток, в котором вызывается метод s e r v i c e . Метод s e r v i c e проверя­
ет тип HTTP-запроса (GET, POST, PUT, DELETE, и т.д.) и вызывает соответствующий ме­
тод (например, doGet, d o P o s t , doPut, d o D e l e t e ) . Если ваш сервлет должен одинаково
реагировать на запрос POST и на запрос GET, то вы, возможно, захотите вместо реали­
зации методов doGet и do P o s t непосредственно переопределить метод s e r v i c e . Од­
нако мы не рекомендуем поступать так. Лучше организовать вызов метода doGet в теле
метода d o P o s t (или наоборот). Пример подобного подхода показан ниже.
p u b l i c void doGet(HttpServletRequest r e q u e s t ,
HttpServletResponse response)
throws S e r v l e t E x c e p t i o n , lOException {
/ / Код сервлета . . .
832 Глава 19. Java на стороне сервера: сервлеты

public void doPost(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, lOException {
doGet(request, response);
}
Несмотря на то что приведенный фрагмент содержит несколько дополнительных
строк кода по сравнению с непосредственной реализацией s e r v i c e , данный подход
обладает рядом преимуществ. Во-первых, впоследствии может возникнуть необходи­
мость реализовать в сервлете обработку других типов запросов, для чего потребуется
переопределить d o P u t , d o T r a c e или другие методы. Если вы переопределите
s e r v i c e , эти методы не получат управление. Во-вторых, в исходной реализации ме­
тода s e r v i c e вызывается метод g e t L a s t M o d i f i e d , который поддерживает дату по­
следнего изменения данных. Переопределив s e r v i c e , вы также лишитесь возможно­
сти использовать данное средство. И, наконец, переопределяя метод s e r v i c e , вы от­
казываетесь от автоматической поддержки запросов HEAD, OPTION и TRACE.

Совет
Если ваш сервлет должен выполнять одинаковую обработку запро­
сов GET И POST, не следует переопределять метод service. Лучше
организовать вызов метода doPost из doGet или вызов метода doGet
ИЗ doPost.

Методы doGety doPost и doXxx


в теле этих методов выполняются основные действия по обработке данных. В по­
давляющем большинстве случаев сервлеты обрабатывают запросы GET и POST, по­
этому основное внимание разработчиков обычно сосредоточивается на реализации
методов d o G e t и d o P o s t . Иногда приходится также переопределять d o P u t для обра­
ботки запроса PUT, d o O p t i o n s — для запроса OPTIONS и d o T r a c e — для поддержки
TRACE. Заметьте, что сервер обеспечивает автоматическую обработку запросов
OPTIONS и TRACE. Обратите также внимание на то, что метод doHead отсутствует.
Для ответа на запрос HEAD система автоматически использует строку состояния и по­
ля заголовка d o G e t ,

Интерфейс SingteThreadModel
Обычно система создает один экземпляр сервлета, а затем по получении запросов
формирует потоки. Во время обработки одного запроса может быть создано несколь­
ко новых потоков. Это означает, что в теле методов d o G e t и d o P o s t необходимо
предусмотреть средства синхронизации доступа к совместно используемым данным.
Если вы хотите запретить одновременное выполнение нескольких потоков, надо соз­
дать класс сервлета так, чтобы он реализовывал интерфейс S i n g l e T h r e a d M o d e l .
public class YourServlet extends HttpServlet
implements SingleThreadModel {

}
19.5. Пример использования инициализированных параметров 833

В этом случае вы получаете гарантию того, что в системе для одного экземпляра
сервлета не будет выполняться более одного потока. Запросы к сервлету помещаются
в очередь и обрабатываются последовательно один за другим. Можно также органи­
зовать пул экземпляров сервлетов и использовать его для обслуживания запросов. В
этом случае исчезает необходимость заботиться о доступе к переменным экземпляра.
Тем не менее вы должны обеспечить корректную обработку переменных класса
(объявленных как s t a t i c ) , а также данных, расположенных за пределами сервлета.
Однопотоковая организация существенно снижает производительность сервлета,
в особенности, если обращения от клиентов поступают достаточно часто. Поэтому
стоит хорошо подумать, прежде чем принимать решение о реализации интерфейса
SingleThreadModel.

Метод destroy
В процессе работы сервер может принять решение о завершении экземпляра серв­
лета. Это может произойти в результате действий администратора либо в случае, если
сервер обнаружит, что сервлет бездействует в течение достаточно длительного време­
ни. Перед тем как завершить работу сервлета, сервер вызывает метод d e s t r o y . Этот
метод дает возможность сервлету закрыть соединения с базой данных, закрыть потоки,
записать данные cookie на диск и выполнить необходимые действия по освобождению
ресурсов. Однако не забывайте о том, что при прекращении работы сервлета не всегда
вызывается метод d e s t r o y . Сервер может выйти из строя, и все данные, не сохранен­
ные на диске, будут утеряны. Поэтому необходимо предусмотреть периодическую за­
пись на диск данных (например, записей cookie) в процессе работы сервера.

19.5. Пример использования


инициализированных параметров
В листинге 19.6 показан код сервлета, который в процессе инициализации читает
параметры m e s s a g e и r e p e a t s . На рис. 19.3 показаны результаты выполнения серв­
лета в случае, когда установлено значение S h i b b o l e t h параметра m e s s a g e , а значе­
ние r e p e a t s равно 5. Сервлет зарегистрирован под именем ShowMsg. В листинге 19.7
приведен XML-код конфигурационного файла, предназначенного для сервера Tomcat
3, а в листинге 19.8 показан конфигурационный файл для JSWDK. Серверы, совмес­
тимые со спецификацией 2.2, используют тот же формат конфигурационного файла,
что и Tomcat, но предоставляют средства генерации таких файлов, обладающие гра­
фическим пользовательским интерфейсом.
При разработке сервлетов желательно сократить объем инициализированных
данных. Это упростит процесс переноса сервлетов с одного сервера на другой. Если
же для инициализации сервлета необходим большой объем информации, лучше по­
местить ее в отдельные файлы, а в составе инициализированных параметров задать
размещение этих фар^лов.
834 Глава 1 9 . Java на стороне с е р в е р а : сервлеты

М е т о д и к а профессионалов

Если объем информации, требуемой для инициализации сервлета,


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

Листинг 1 9 . 6 . S h o w M e s s a g e . J a v a

p a c k a g e cwp;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Пример инициализации сервлета. В качестве инициализационных


* параметров передаются сообщение и число повторов
* этого сообщения.
V
public class ShowMessage extends HttpServlet {
private String message;
private String defaultMessage = "No message.";
private int repeats = 1;

public void initO throws ServletException {


ServletConfig config = getServletConfigO;
message = config.getlnitParameter("message");
if (message == null) {
message = defaultMessage;
}
try {
String repeatString = config.getlnitParameter("repeats");
repeats ~ Integer.parseint(repeatString);
} catch(NumberFormatException nfe) {
// Исключение NumberFormatException генерируется
// тогда, когда значение параметра repeatString
// равно null, и в том случае, если задан некорректный
// формат этого параметра.
}
}

public void doGet(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, lOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "The ShowMessage Servlet";
out.printIn(ServletUtilities.headWithTitie(title) +
"<BODY BGC0L0R=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</Hl>");
for(int i=0; i<repeats; i++) {
out.println("<B>" + message + "</B><BR>");
}
19.5. Пример использования инициализированных параметров 835

out.println("</BODY></HTML>");

Щ T h e ShowpMessage S e i v l e l - N e t s c a p e

fie £dit y*ew go С^мптид-асйо» Hefe>

i ^ -^^ 1 r^i ^ ^ 't-:s rf a ai / Я1


^ j ^ ' Boc^m*fk« ^ Locetwn j http- //localhost/sefvtet/S howM $g •» |

The ShowMessage Sei*\1et


Shibboleth
Shibboleth
Shibboleth Рис. 79.3. Сервлет ShowMessage,
Slubboleth зарегистрированный под именем showMsg
Sliibboleth
и инициализированный указанным набором
.шш,,тшШшШ1 параметров

Л и с т и н г 1 9 . 7 . w e b . x m l (для T o m c a t 3 )

<?xml v e r s i o n = " 1 . 0 " encoding="ISO-8859-l"?>


<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://j ava.sun.com/j 2ee/dtds/web-app_2.2.dtd">

<web-app>
<servlet>
<servlet-name>ShowMsg</servlet-name>
<servlet-class>cwp.ShowMessage</servlet-class>
<init-param>
<param-name>message</param-name>
<param-value>Shibboleth</param-value>
</init-param>
<init-param>
<param-name>repeats</param-name>
<param-value>5</param-value>
</init-param>
</servlet>
</web-app>

Листинг 19.8. servlets, properties (для J S W D K 1.0.1)

# servlets.properties для использования JSWDK

# Регистрация сервлета в формате servletName.code=servletClassFile


# Доступ посредством http://host/examples/servlet/servletName
ShowMsg.code=cwp.ShowMessage

Установка инициализированных параметров в виде:


servletName.initparams=paraml=vall,param2=val2,...
836 Глава 19. Java на стороне сервера: сервлеты

ShowMsg.initparams=message=Shibboleth,repeats=5

# Стандартная установка
j sp.code=com.sun.j sp.runtime.JspServlet

# Сохранение исходного кода сервлета, построенного на базе JSP


j sp.initparams=keepgenerated=true

19.6. Запрос клиента: данные формы


Одной из причин, по которой разработчики приступают к написанию программ,
предназначенных для динамического создания Web-страниц, является возможность
учитывать данные, введенные пользователем, при формировании ответа. В данном
разделе описаны способы получения этих данных.
Если вы когда-либо обращались к поисковому серверу, приобретали товары через
Internet-магазин, просматривали котировки акций на бирже или заказывали по сети
авиационные билеты, вы наверняка видели URL, подобные следующему:
h t t p : / / h o s t / p a t h ? u s e r = M a r t y + H a l l & o r i g i n = b w i & d e s t = l a x . Символы, следую­
щие за знаком вопроса (в данном случае u s e r = M a r t y + H a l l & o r i g i n = b w i & d e s t = l a x ) ,
принято называть данными формы, или данными запроса. Эти данные чаще всего при­
меняются для передачи и н ф о р м а ц и и Web-клиентом программе, выполняющейся на
стороне сервера. Данные ф о р м ы присоединяются к URL после знака вопроса (в слу­
чае запроса GET) либо передаются в отдельной строке (в случае запроса POST). Под­
робно вопросы создания HTML-форм и передачи информации на сервер были рас­
смотрены в главе 18.

Получение данных формы CGI-программами


Извлечение информации из данных ф о р м ы — одна из рутинных задач Web-
программирования, которые приходится решать разработчикам программ, предна­
значенных для выполнения на стороне сервера. Прежде всего, надо уметь получать
данные, содержащиеся в запросе GET (согласно стандарту CGI, они передаются как
значение переменной окружения QUERY_STRING) и запросе POST (CGI-программам
они передаются через стандартный ввод). Далее надо выполнить разбор строки па­
раметров, выделить пары имя-значение и в каждой паре отделить значение параметра
от его имени. После этого следует выполнить декодирование значений параметров.
Латинские буквы и ц и ф р ы передаются клиентом без изменений, пробел заменяется
знаком "+", а остальные символы преобразуются в последовательности %ХХ, где XX —
двузначное шестнадцатеричное число, представляющее код символа.

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


Одним из преимуществ сервлетов является автоматический разбор строки пара­
метров. Для того чтобы получить значение параметра, надо вызвать метод g e t -
P a r a m e t e r класса H t t p S e r v l e t R e q u e s t . П р и вызове методу g e t P a r a m e t e r переда­
ется имя параметра (с учетом регистра символов). Порядок вызова g e t P a r a m e t e r не
19.6. Запрос клиента: данные формы 837

зависит от того, были ли данные переданы методом GET или методом POST. Сервлет
выясняет использованный метод и автоматически выполняет разбор строки пара­
метров. Д а н н ы й метод возвращает объект S t r i n g , содержащий значение, которое
соответствует первому вхождению указанного имени в строку параметров. Если пара­
метр существует, но с ним не связано никакое значение, возвращается пустая строка.
Если при вызове метода указано имя несуществующего параметра, метод возвращает
значение n u l l . Как вы уже знаете, в составе строки параметров могут присутствовать
несколько пар и м я - з н а ч е н и е с совпадающими именами. Для обработки таких данных
желательно использовать вместо g e t P a r a m e t e r метод g e t P a r a m e t e r V a l u e s , воз­
вращающий массив строк. Если указанный параметр отсутствует, метод g e t ­
P a r a m e t e r V a l u e s возвращает значение n u l l , а если имя встречается в строке пара­
метров один раз, метод возвращает массив, состоящий из одного элемента.
Имена параметров зависят от регистра символов, поэтому вызовы методов
request.getParameter("Paraml") и request.getParameter("paraml") не­
идентичны.
Внимание!
Параметры, передаваемые методам getParameter и getParameter­
Values, зависят от регистра символов.

Несмотря на то что реальные сервлеты ожидают пост)Т1ления параметров с опре­


деленными именами, при отладке программы удобно получать полный список пара­
метров. Это можно сделать с помощью метода g e t P a r a m e t e r N a m e s , возвращающего
набор имен, в виде объекта E n u m e r a t i o n . Элементы E n u m e r a t i o n можно привести к
типу S t r i n g и использовать при вызове методов g e t P a r a m e t e r и g e t P a r a m e t e r ­
V a l u e s . Заметьте, что в описании API H t t p S e r v l e t R e q u e s t не оговорен порядок, в
котором параметры должны включаться в состав объекта E n u m e r a t i o n .

Пример получения значений трех параметров


в листинге 19.9 показан простой сервлет T h r e e P a r a m s , который в процессе вы­
полнения читает значения трех параметров с именами p a r a m l , p a r a m 2 и р а г а т З .
Полученные значения сервлет возвращает клиенту в виде маркированного списка.
В листинге 19.10 представлена HTML-форма, с помощью которой пользователь вво­
дит информацию. Введенные данные передаются сервлету. URL, заданный в качестве
значения атрибута ACTION, начинается с косой черты ( / s e r v l e t / c w p . T h r e e -
Pa г ams), это означает, что Web-страница с ф о р м о й может находиться в любом ката­
логе того компьютера, на котором выполняется сервлет. На серверах, поддерживаю­
щих спецификацию Java Servlet 2.2, HTML-файлы (а также изображения и JSP) поме­
щаются в один из каталогов, родительских по отношению к каталогу, содержащему
WEB-INF. Н а п р и м е р , для сервера Tomcat таким каталогом является i n s t a l l d i r /
webapps/ROOT, а для J R u n — i n s t a l l _ d i r / s e r v e r s / d e f a u l t / c i e f a u l t - a p p . Для
более старых серверов стандартное расположение файлов не определено (например,
JSWDK использует каталог i n s t a l l _ d i r / w e b p a g e s ) . Заметьте, что в сервлете
T h r e e P a r a m s чтение данных, передаьп1ых в составе запроса, производится тогда, ко­
гда генерация ответа уже началась. Как вы помните, поля заголовка должны бьггь ус-
838 Глава 19. Java на стороне сервера: сервлеты

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


начала обработки параметров.
На рис. 19.4 и 19.5 показаны внешний вид исходного HTML-документа и результа­
ты выполнения сервлета.

Листинг 19.9. T h r e e P a r a m s . j a v a

package cwp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ThreeParams extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Reading Three Request Parameters";
out.printIn(ServletUtilities.headWithTitie(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</Hl>\n"
"<UL>\n" +
" <LI><B>paraml</B>: "
+ request.getParameter("paraml") + "\n" +
" <LI><B>param2</B>: "
+ request.getParameter("param2") + "\n" +
<LI><B>param3</B>: "
+ request.getParameter("рагатЗ") + "\n" +
"</UL>\n" +
"</BODY></HTML>");
}
}

Листинг 19.10. ThreeParamsForm.html

<!DOCTYPE HTML PUBLIC "-//W5C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Collecting Three Parameters</TITLE>
</HEAD>
<BODY BGC0L0R="#FDF5E6">
<H1 ALIGN="CENTER">Collecting Three Parameters</Hl>
<FORM ACTION="/servlet/cwp.ThreeParams">
First Parameter: <INPUT TYPE="TEXT" NAME="paraml"><BR>
Second Parameter: <INPUT TYPE="TEXT" NAME="param2"><BR>
Third Parameter: <INPUT TYPE="TEXT" NAME="param3"><BR>
<CENTER><INPUT TYPE="SUBMIT"></CENTER>
</FORM>
</BODY>
</HTML>
19.6. Запрос клиента: данные формы 839

[t^iffiifflffiimiFmffi«nmnff,migffii^ii^^i—i 1Я
; Ftle £drt View Г^уоЛег Tools Нф

" 3 r^Go I

Collecting Three Parameters


1 First Parameter: |~haii
1 Second Parameter: |~brown
Third Parameter j~mcnemee
SMbmliOuejy |
J

Рис. 19.4. HTML-форма, содержащаяся в составе документа


ThreeParamsForm.html

ь\тт\т;т\1Щ
Frfe £dtt View F§vontes loots H d p

', Arfdiess JC] http //localhostAefvlet/cwp ThieePd(afns'!'pafam1=;i7Ehall&pafam2=5i7Ebtown?tpa«am3=J^7Emcr»amee J J p^Go

""1^
Reading Three Request Parameters
• paimnl: ~hall
• paiaml: -brown
• paiamS: -mcnamee

jj
^Oone ~щ Local итйате»

Рис. / 9 . 5 . Выходные данные сервлетаТЬгееРагатз

Если вы имеете опыт традиционного CGI-программирования, вы, наверное, при­


выкли читать данные, передаваемые методом POST из стандартного ввода. В сервлете
вы можете сделать т о ж е самое. Для этого надо вызвать метод g e t R e a d e r или
g e t l n p u t S t r e a m класса H t t p S e r v l e t R e q u e s t , а затем использовать полученный
поток для чтения необработанных выходных данных. Несмотря на наличие подоб­
ной возможности, пользоваться ею вряд л и следует. Метод g e t P a r a m e t e r гораздо
удобнее, так как возвращает декодированную информацию. Пользоваться входным
потоком можно разве ч т о п р и приеме файлов или данных, передаваемых нестан­
дартным клиентом посредством метода POST. Заметьте, что если прочитать из стан­
дартного ввода POST-данные, они становятся недоступны методу g e t P a r a m e t e r .

Фильтрация данных, полученных в составе запроса


в рассмотренном примере значения параметров p a r a m l , p a r a m 2 и р а г а т З вклю­
чаются без изменений в состав HTML-документа, возвращаемого клиенту. Такой под­
ход может привести к нежелательным последствиям, поскольку значения параметров
могут содержать в себе символы, интерпретируемые специальным образом, напри­
мер "<". Появление такого символа повлияет на внешний вид всей Web-страницы, по-
840 Глава 19. Java на стороне сервера: сервлеты

скольку следующий за ним дескриптор будет обработан некорректно. Поэтому, перед


тем как включать значения параметров в состав Web-страницы, желательно прове­
рить их и преобразовать специальные HTML-символы. В листинге 19.11 показан код
статического метода f i l t e r , выполняющего подобное преобразование.

Листинг 1 9 . 1 1 . S e r v l e t U t i l i t i e s . J a v a

p a c k a g e cwp;
import javax.servlet.*;
import javax.servlet.http.*;
public class ServletUtilities {
// Другие части ServletUtilities здесь не показаны.
/** Данный метод заменяет все вхождения символа "<" на
* "&lt;", все вхождения ">" на "&gt;" и (для значений
* атрибутов) все вхождения двойных кавычек на
* "&quot;", а все вхождения символа "&" на "&атр;".
* Без подобной фильтрации символы, встречающиеся в строке,
* могут повлиять на внешний вид Web-страницы.

public static String filter(String input) {


StringBuffer filtered = new StringBuffer(input.length());
char c;
for(int i=0; i<input.length(); i++) {
с = input.charAt(i);
if (c == '<') {
filtered.append("&lt;") ;
} else if (c == •>') {
filtered.append("&gt;");
} else if (c == " " ) {
filtered.append("&quot;")/
} else if (c == '&') {
filtered.append("&amp;");
} else {
filtered.append(c);
}
}
return(filtered.toString()) ;

19.7. Запрос клиента: заголовок


HTTP-запроса
Для того чтобы создавать э ф ф е к т и в н ы е сервлеты, надо хорошо понимать работу
HTTP (HypcrText Transfer Protocol — протокол передачи гипертекстовой информа­
ции). Рассмотрение особенностей данного протокола— это не отвлеченные теорети­
ческие рассуждения, а решение задач, позволяюш,их повысить производительность
19.7- Запрос клиента: заголовок HTTP-запроса 841

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


обращении к серверу. Вы узнаете, каким образом сервлет может использовать ин­
формацию, передаваемую в полях заголовка, предусмотренных протоколом H T T P
1.1. Здесь будут также приведены два примера сервлетов. Один из них отображает все
данные, пол)^ченные от броузера в полях заголовка, а другой уменьшает время копи­
рования Web-страницы на компьютер клиента, передавая ее в сжатом виде.
Следует различать данные ф о р м ы , рассмотренные в предыдущем разделе, и поля
заголовка HTTP-запроса. Данные ф о р м ы формируются на основе информации, вве­
денной пользователем, и присоединяются к URL либо передаются в отдельной стро­
ке. Поля заголовка создаются броузером и непосредственно следуют за строкой, со­
держащей название метода (GET или POST). В приведенном ниже примере показан
HTTP-запрос, сгенерированный в результате действий пользователя, обращающегося
к поисковой программе книжного магазина h t t p : / / w w w . s o m e b o o k s t o r e . c o m /
s e r v l e t / S e a r c h . Заголовок запроса содержит поля A c c e p t , A c c e p t - E n c o d i n g ,
C o n n e c t i o n , C o o k i e , H o s t , Ref e r e r и U s e r - A g e n t . Bee они могут быть использова­
ны сервлетом, но ни одно из них не связано с данными, введенными пользователем в
форме. Чтобы воспользоваться переданными данными, сервлет должен прочитать
значения полей заголовка.
GET / s e r v l e t / S e a r c h ? k e y w o r d s = s e r v l e t s + j s p HTTP/1.1
A c c e p t : i m a g e / g i f , i m a g e / j p g , */"*"
Accept-Encoding: gzip
Connection: Keep-Alive
Cookie: userID=id456578
Host: www.somebookstore.com
Referer: http://www.somebookstore.com/findbooks.html
U s e r - A g e n t : M o z i l l a / 4 . 7 [en] (Win98; U)

Чтение полей заголовка сервлетами


П р о ч и т а т ь значение поля заголовка несложно: для этого надо вызвать метод
g e t H e a d e r класса H t t p S e r v l e t R e q u e s t ; в качестве параметра при вызове метода
указывается его имя. Если имя задано корректно, метод g e t H e a d e r возвращает объ­
ект S t r i n g , содержащий значение поля, в противном случае возвращается значение
n u l l . Имена полей заголовка не зависят от регистра символов. Например, в результа­
те вызовов r e q u e s t . g e t H e a d e r ( " C o n n e c t i o n " ) и r e q u e s t . g e t H e a d e r ( " c o n n e c ­
t i o n " ) будут получены одинаковые результаты.
Несмотря на то что g e t H e a d e r позволяет получать значения любых полей заго­
ловка, для чтершя содержимого некоторых полей в классе H t t p S e r v l e t R e q u e s t
предусмотрены специальные методы, описанные ниже.
• getCookies
Метод g e t C o o k i e s возвращает содержимое поля C o o k i e . П р и получении зна­
чения выполняется его синтаксический разбор и результаты оформляются в
виде массива объектов C o o k i e . Данный метод будет подробно рассмотрен в
разделе 19.1.
• getAuthType и getRemoteUser
Методы g e t A u t h T y p e и g e t R e m o t e U s e r выделяют компоненты поля
Authorization.
842 Глава 19. Java на стороне сервера: сервлеты

• getContentLength
Метод g e t C o n t e n t L e n g t h возвращает значение заголовка C o n t e n t - L e n g t h в
целочисленном виде ( i n t ) .
• getContentType
Метод g e t C o n t e n t T y p e возвращает значение заголовка C o n t e n t - T y p e (в виде
объекта S t r i n g ) .
• getDateHeader и getlntHeader
Методы g e t D a t e H e a d e r и g e t l n t H e a d e r читают указанные поля и преобра­
зуют их значения соответственно в типы D a t e и i n t .
• getHeaderNames
Вместо обращения к конкретному полю заголовка по его имени вы можете ис­
пользовать метод g e t H e a d e r N a m e s для получения объекта E n u m e r a t i o n , со­
держащего все поля, имеющиеся в составе текущего запроса. Эту возможность
иллюстрирует код, приведенный в листинге 19.12.
• getHeaders
В большинстве случаев имя поля появляется в заголовке запроса лишь один раз.
Однако имена некоторых полей могут повторяться в составе запроса с различ­
ными значениями. Примером такого поля может служить A c c e p t - L a n g u a g e .
Если сервер поддерживает спецификацию Java 5е1Л'1е1 2.2, вы можете вызывать
метод g e t H e a d e r s , который возвращает объект E n u m e r a t i o n , содержащий
все значения полей с указанным именем.
Помимо чтения значений полей заголовка, вы можете получить информацию о
самом запросе. Для этого также используются методы объекта H t t p S e r v l e t R e q u e s t .
Т р и основных метода, предназначенных для этой цели, описаны ниже.

• getMethod
Метод g e t M e t h o d возвращает метод запроса (обычно это GET или POST, но мо­
гут также встречаться методы HEAD, PUT и DELETE).
• getRequestURI
Метод g e t R e q u e s t U R I возвращает часть URL, расположенную после адреса
узла и номера порта перед данными формы. Н а п р и м е р , для URL h t t p : / /
r a n d o m h o s t . c o m / s e r v l e t / s e a r c h . B o o k S e a r c h метод g e t R e q u e s t U R I вер­
нет з н а ч е н и е / s e r v l e t / s e a r c h . B o o k S e a r c h .
• getProtocol
Метод g e t P r o t o c o l возвращает сведения о протоколе, содержащиеся в первой
строке HTTP-запроса. Обычно в результате выполнения данного метода возвра­
щается строка H T T P / 1 . 0 или H T T P / 1 . 1 . Перед тем как включать в состав ответа
поля, специфические для протокола HTTP 1.1, надо вызвать g e t P r o t o c o l и убе­
диться, что броузер поддерживает версию 1.1 данного протокола.
19.7. Запрос клиента: заголовок HTTP-запроса 843

Создание таблицы, содержащей полученные


поля заголовка
в листинге 19.12 представлен код сервлета, который создает таблицу, содержащую
полученные поля заголовка и их значения. Он также выводит т р и основных компо­
нента первой строки запроса (метод, URI и протокол). Результаты выполнения серв­
лета, отображаемые в окнах броузеров Netscape и Internet Explorer, показаны на
рис. 19.6 и 19.7.

Листинг 1 9 . 1 2 . S h o w R e q u e s t H e a d e r s . J a v a

p a c k a g e cwp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
/•• Отображение полей заголовка, переданных в составе запроса.
public class ShowRequestHeaders extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
response.setContentType{"text/html");
PrintWriter out = response.getWriter();
String title = "Servlet Example: Showing Request Headers";
out.printIn(ServletUtilities.headWithTitle(title) +
"<BODY BGC0L0R=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</Hl>\n" +
"<B>Request Method: </B>" +
request. getMethodO + "<BR>\n" +
"<B>Request URI: </B>" +
request.getRequestURI0 + "<BR>\n" +
"<B>Request Protocol: </B>" +
request.getProtocolO + "<BR><BR>\n" +
"<TABLE B0RDER=1 ALIGN=\"CENTER\">\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
"<TH>Header Name<TH>Header Value");
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
out.println("<TR><TD>" + headerName);
out.println(" <TD>" + request.getHeader(headerName));
}
out.println("</TABLE>\n</BODY></HTML>");
}
/** Сервлет поддерживает как метод GET, так и метод POST. */
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
844 Глава 19. Java на стороне сервера: сервлеты

doGet(request, response);

wsam Shfnwtng Bequest Headefs - Netscape


ШШ
£ie £d» View &о Cofrtfiiuntcatoi >:d
ЦеЬ-.j^ ё' -:J ss
^^ У Sei'^let Example: Showing Request Headers
3 !^i} J^.

Request Method: GET


Request URI: /sendet/cwp.ShowRcquestHeaders
Request Protocol: HTTP/1.0

Header Name
Accept Language f;.
Connection Keep-Alive
User-Agent MozillaM 7 [en] (Wm98. U)
Accept-Charset iso-8859-l,*,utf-8
iHost localhost
jAccept-Encoding ;gzip
:Accept ^age/gif; unagc/x-xbitamp, miagc/jpcg, miagc/pjpeg. cnage/png, */*

Q# -"J^ Сосш>егй: Done j<r',:.,i:s.:-;e^:

Рис. 19.6. Поля заголовка в окне броузера Netscape 4.7, выполняющегося


в системе Windows 98

t^tflnfiifmi^mffiif^i!H«iw^inmiinimif'
'• Ffe £{Й View F^vofites ТоЫе Нф

3,
Sei^let Example: Showing Request Headers
Request Metliod; GET
Request URI: /servlet/cwp.ShowRequestHeaders
Request Rotocol; HTTP/1 1

:|||Ш1Ш||Ши'р
Accept-
:- :en-us
iLanguage
iConnection -Keep-AHvc
;User-Agent ;Моа11а/4.0 (compatible. MSIE 5.0, Windows 9S, DigExt)
iHost ilocalhost
^Accept-
jgap, deflate
;Encoding
'image/gif, image/x-xbitmap, image/jpeg. image/pjpeg, application/msword,
jAccept
appbcaton/vndms-excel, appHcabon/^/nd ms-poweфOlnt, *'*•

€30one ~Ц LOCi» вТЦЦГЩЯ

Рис. 19.7. Поля заголовка в окне броузера Internet Explorer 5.0, выпол­
няющегося в системе Windows 98
19.7. Запрос клиента: заголовок HTTP-запроса 845

Поля заголовка, определенные в протоколе


HTTP 1.1
Наличие доступа к полям заголовка позволяет разработчикам сервлетов предпри­
нимать различные меры по оптимизации работы своих программ. Н и ж е описаны по­
ля, наиболее часто используемые сервлетами. Исчерпывающие сведения об этих и
других полях вы найдете в спецификации H T T P 1.1, содержащейся в документе RFC
2616. Архивы RFC находятся в Internet по различными адресам. Получить список ар­
хивов можно по адресу h t t p : //www. r f c - e d i t o r . o r g / .

Accept
В данном поле содержатся MIME-типы данных, поддерживаемых броузером или
другим HTTP-клиентом, отправившим запрос. Сервлет, в котором предусмотрена
возможность передачи данных в различных форматах, проверяет содержимое
A c c e p t и решает, какой из форматов целесообразно выбрать при ф о р м и р о в а н и и
ответа. Н а п р и м е р , изображения в формате PNG занимают меньше места по срав­
нению с теми же изображениями в формате GIF, однако броузеры, поддерживаю­
щие формат PNG, встречаются достаточно редко. Если сервлет имеет, доступ к
PNG-файлам, он должен вызвать метод r e q u e s t . g e t H e a d e r ( " A c c e p t " ) , прове­
рить, содержится ли в списке МШЕ-типов i m a g e / p n g , и если такой тип обнару­
жен, то передать файл xxx.png. В противном случае сервлет должен отправить
клиенту файл ххх. g i f . Н е к о т о р ы е MIME-типы приведены в табл. 19.1.

Accept-Charset
Данное поле определяет наборы символов (например, ISO-8859-1), поддерживае­
мые броузером.

Accept-Encoding
Данное поле заголовка указывает типы кодировки, поддерживаемые броузером.
Если сервер пол)'чает поле A c c e p t - E n c o d i n g в составе заголовка, он может зако­
дировать содержимое документа, представив его в любом из указанных форматов.
П р и этом в заголовок ответа включается поле C o n t e n t - E n c o d i n g . Обычно коди­
рование производится для уменьшения времени передачи документа по сети. Тип
кодировки существенно отличается от MIME-типа документа (который указывает­
ся в поле заголовка ответа C o n t e n t - Т у р е ) , так как броузер предпринимает кон­
кретные действия по воспроизведению документа уже после декодирования. Если
броузер не поддерживает конкретный тип кодировки, он не сможет отобразить
Web-страницу. Стандартными типами кодировок являются g z i p и c o m p r e s s .
Сжатие содержимого документа перед отправкой его по сети вполне оправдано,
поскольку время, которое экономится при передаче, значительно превышает вре­
мя, необходимое для декодирования документа. Далее в этом разделе будет приве­
ден пример того, как при кодировании время передачи документа сокращается
приблизительно в 10 раз.
846 Глава 19. Java на стороне сервера: сервлеты

Accept-Language
Если сервлет имеет возможность генерировать ответы на различных языках, то,
проверив содержимое этого поля, он может выбрать язык, подходящий для кли­
ента. Значениями данного поля являются двухбуквенные сокращения, например
en, e n - U S , d a и т.д. Подробно этот вопрос описан в документе RFC 1766.

Authorization
Данное поле идентифицирует клиента при обращении к Web-страницам, защи­
щенным паролем.

Connection
Содержимое данного поля определяет, может ли клиент поддерживать постоян­
ные HTTP-соединения. Посредством одного постоянного соединения броузер
может получить несколько файлов (например, HTML-документ и связанные с ним
изображения). П р и этом исключаются накладные расходы, связанные с открыти­
ем новых соединений. В запросах H T T P 1.1 постоянные соединения предполага­
ются по умолчанию, и для того, чтобы организовать HTTP-обмен в соответствии с
использовавшимся ранее стандартом, надо задать в поле C o n n e c t i o n значение
c l o s e . В H T T P 1.0 значение K e e p - A l i v e указывает на то, что постоянное соеди­
нение должно поддерживаться.
Каждый HTTP-запрос приводит к очередному вызову сервлета, независимо то то­
го, передаются ли запросы в рамках одного соединения или через отдельные со­
единения. Сервер вызывает сервлет только после чтения HTTP-запроса. Это озна­
чает, что для поддержки постоянных соединений сервлет должен пользоваться
средствами сервера. Задача же сервлета— предоставить серверу данные, необхо­
димые для того, чтобы такая поддержка стала возможной. В частности, в составе
ответа, передаваемого через постоянное соединение, обязательно должно присут­
ствовать поле C o n t e n t - L e n g t h .

Content-Length
Данное поле используется только в запросах POST. Содержимое поля указывает чис­
ло байт в строке. Вместо того чтобы вызывать r e q u e s t . g e t l n t H e a d e r ( " C o n t e n t -
L e n g t h " ) , вы можете обратиться в методу r e q u e s t . g e t C o n t e n t L e n g t h ( ) . По­
скольку сервлеты автоматически выполняют разбор данных, обращение к данному
полю выполняется очень редко.

Cookie
В поле C o o k i e броузер возвращает серверу данные cookie, которые ранее были
переданы ему. Подробно этот вопрос рассматривается в разделе 19.11. Поддержка
cookie не предусмотрена в протоколе H T T P 1.1. Cookie представляет собой рас­
ширение Netscape, которое в настоящее время поддерживается как Netscape, так и
Internet Explorer.
19.7. Запрос клиента: заголовок HTTP-запроса 847

Host
Броузеры и другие HTTP-клиенты обязаны включать данное поле в заголовок за­
проса. Содержимое поля Host определяет узел и порт, соответствующие исходно­
му URL. Так как компьютеры могут иметь несколько имен и поскольку запросы в
некоторых случаях перенаправляются, может возникнуть ситуация, при которой
поле Host станет единственным источником информации об узле. Данное поле
использовалось и раньше, однако в HTTP 1.0 оно было необязательным.

If-Modified-Since
Данное поле заголовка указывает на то, что документ должен передаваться клиен­
ту только в том случае, если он был модифицирован позже указанной даты. Если
новые результаты не получены, сервер передает в заголовке код 302 (Not
Modified). Наличие поля If-Modif i e d - S i n c e позволяет броузеру сохранять до­
кументы в кэше, передавая их по сети только в том случае, если с момента послед­
него просмотра они были изменены. Сервлет не должен непосредственно выпол­
нять какие-либо действия с этим заголовком. Чтобы система могла автоматически
принимать решение о передаче документа, достаточно реализовать в сервлете ме­
тод g e t L a s t M o d i f i e d .

If-Unmodified-Since
Данное поле вызывает результат, обратный If-Modif i e d - S i n c e . Оно указывает
на то, что обработка запроса должна быть продолжена только в том случае, если
документ был создан или модифицирован раньше указанной даты. Если поле If-
Modif i e d - S i n c e применяется в запросах GET (передать документ только в том
случае, если он модифицирован позже, чем документ, содержащийся в кэше), то
поле If-Unmodif i e d - S i n c e включается в состав запросов PUT (обновить доку­
мент только в том случае, если он имеет более раннюю дату последней модифика­
ции, чем предлагаемый документ).

Referer
Данное поле содержит URL Web-страницы, на которой находится ссылка, исполь­
зованная при формировании запроса. Так, например, если на странице А содер­
жится ссылка на страницу Б, то в составе запроса на получение страницы Б будет
содержаться поле R e f e r e r , в котором указан URL страницы А. Эта возможность
позволяет выявить документы, авторы которых согласились включить в них рек­
ламу других узлов, а также дает возможность прослеживать трафик. Значение
R e f e r e r обычно отражается в файле протокоа. Несмотря на то что R e f e r e r —
очень полезное поле, не стоит излишне полагаться на него, поскольку его значе­
ние легко фальсифицировать. Заметьте также, что поле называется R e f e r e r (а не
R e f e r r e r , как можно было бы ожидать); один из авторов HTTP допустил грамма­
тическую ошибку.

User-Agent
Данный заголовок идентифицирует броузер, сгенерировавший запрос, и может
быть использован для того, чтобы менять содержимое документа в зависимости от
типа применяемого клиента. Создавая версии документов или коды сервлетов,
848 Глава 1 9 . Java на стороне с е р в е р а : сервлеты

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


их очень трудно сопровождать. Вместо того чтобы запоминать особенности бро­
узеров, старайтесь использовать поля HTTP-заголовка. Например, нежели ориен­
тироваться на типы броузеров, поддерживающих кодировку g z i p , лучше прове­
рять поле заголовка A c c e p t - E n c o d i n g .
Как правило, Internet Explorer включает в поле U s e r - A g e n t идентификатор
" M o z i l l a " , указывая реальную версию броузера в скобках. Это делается для того,
чтобы обеспечить JavaScript-совместимость.

Передача сжатых ]/\/еЬ'Страниц


Ряд современных броузеров поддерживает сжатые док)'менты. Полу^1ив Web-
страницу, помеченную соответствующим образом в заголовке C o n t e n t - E n c o d i n g ,
они автоматически распаковывают ее и обрабатывают как обычный Web-pecypc. Сжа­
тие и послед)^ющее декодирование не приводят к замедлению обмена, напротив, вре­
мя на обработку документа оказывается намного меньше, чем время, сэкономленное в
результате передачи сжатых данных. Особенно это заметно при взаимодействии че­
рез низкоскоростные т е л е ф о н н ы е линии.
Обработку сжатых документов поддерживают многие версии Netscape для UNIX,
Netscape 4.7 и более поздние версии этого броузера для Windows, а также большинст­
во версий Internet Explorer для Windows. Эти броузеры сообщают о своих возможно­
стях, передавая в составе запроса поле заголовка A c c e p t - E n c o d i n g . В листинге 19.13
и на рис. 19.8 показан сервлет, который проверяет этот заголовок, передает клиенту,
поддерживающему g z i p , сжатую Web-страницу, а клиент)', не имеющему такой воз­
можности, — обычный HTML-документ. Тестирование с броузерами Netscape 4.7 и
Internet Explorer 5.0 на компьютере, подключенном по линии 28,8 К б и т / с , показало,
что сжатая версия документа передается менее чем за 5 секунд, в то время как копи­
рование обычной Web-страницы занимает более 50 секунд.
Совет

Сжатие позволяет существенно снизить время передачи больших


документов.

Реализация сжатия не вызывает затруднений, поскольку язык Java обладает встро­


енными средствами поддержки формата g z i p , содержащимися в пакете J a v a .
u t i l . z i p . Сервлет проверяет содержимое поля A c c e p t - E n c o d i n g и выясняет, на­
ходится ли в нем запись g z i p . Если такая запись найдена, для генерации страницы
используется поток G Z I P O u t p u t S t r e a m , а в состав ответа включается поле C o n t e n t -
E n c o d i n g . П р и м е н я я поток G Z I P O u t p u t S t r e a m , необходимо явно вызывать метод
c l o s e . Если кодировка g z i p не поддерживается, сервлет использует для передачи
страницы поток P r i n t W r i t e r . Для сравнения времени передачи одному и тому же
броузеру сжатых и несжатых документов мы присоединили к URL запись
? e n c o d i n g = n o n e , подавляющую сжатие.
19.7. Запрос клиента: заголовок HTTP-запроса 849

Листинг 1 9 . 1 3 . E n c o d e d P a g e . J a v a

p a c k a g e cwp;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.zip.*;

/** Демонстрация преимуществ передачи сжатых страниц броузерам,


* поддерживающим gzip.
V
public class EncodedPage extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
response.setContentType("text/html");
String encodings = request.getHeader("Accept-Encoding");
String encodeFlag = request.getParameter("encoding");
PrintWriter out;
String title;
if ((encodings != null) &&
(encodings.indexOf("gzip") != -1) &&
!"none".equals(encodeFlag)) {
title = "Page Encoded with GZip";
Outputstream outl = response.getOutputStreamO;
out = new PrintWriter(new GZIPOutputStream(outl), false);
response . setHeader ("Content-Encoding" , "gzip") ;"
} else {
title =•• "Unencoded Page";
out = response.getWriter0;

out.printIn(ServletUtilitles.headWithTitie(title) +
"<BODY BGC0L0R=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</Hl>\n"
String line = "Blah, blah, blah, blah, blah. " +
"Yadda, yadda, yadda, yadda.";
for(int 1=0; i<10000; i++) {
out.println(line);
}
out.println("</BODY></HTML>");
out.close 0 ;
850 Глава 19. Java на стороне сервера: сервлеты

^ Page EnGoded «with G2Sp - Netscape


E*5 gdf Vie*»/ Go CofnmunJcator Й«^Р

- ^ y^ J : . i ^ j i i ^ . i ^ r f ' a a
Page Encoded with GZip
Blah, blah, blah, blah, blah Yadda, yadda, yadda, yadda Blah, blah, blah, blah, blah
Yadda, yadda, yadda, yadda Blah, blah, blah, blah, blah Yadda, yadda. yadda, yadda
Blali. blah, blah, blah, blah Yadda, yadda, yadda. yadda Blah, blah, blah, blah, blah j ^

Рис. 19.8. Поскольку Windows-версия Netscape 4.7 под­


держивает g z i p (см. рис. 19.6), данную страницу можно
передавать по сети в сжатом виде; при этом время
копирования существенно сокращается

19.8. Аналоги стандартных CGI-переменных


Если вы уже имеете о п ы т написания традиционных CGI-программ, то, вероятно,
привыкли использовать так называемые CGI-перменные. Это набор переменных ок­
ружения, содержащих информацию о текущем запросе. Н е к о т о р ы е из CGI-
переменных формируются на основе данных, содержащихся в заголовке запроса, дру­
гие предоставляют сведения о гнезде (например, IP-адрес узла); есть среди них пере­
менные, значения которых отражают параметры сервера (например, правила ото­
бражения URL в реальный путь к файлу).
Наверное, имеет смысл рассматривать источники информации, доступные для
сервлета, вне связи их с CGI-программами, однако многим программистам проще ос­
ваивать сервлеты, проводя аналогии с известными им CGI-переменными. Если вам не
приходилось писать CGI-программы, считайте, что вы ничего не потеряли: сервлеты
проще в использовании, обеспечивают большую гибкость и эффективность по срав­
нению со стандартными CGI-программами. Так, например, для того, чтобы выяснить
реальный путь, соответствующий URI (части URL, следующей за адресом узла и но­
мером порта), достаточно вызвать метод g e t S e r v l e t C o n t e x t () . g e t R e a l P a t h , а
определить имя и IP-адрес удаленного клиента можно вызовом методов r e q u e s t .
getRemoteHost и request.getRemoteAddress.

AUTH_TYPE
Если в заголовке присутствует поле A u t h o r i z a t i o n , данная переменная окруже­
ния сообщает об используемой схеме ( b a s i c или d i g e s t ) . Аналогичные данные
можно получить с помощью метода r e q u e s t . g e t A u t h T y p e ( ) .

CONTENT_LENGTH
Значение данной переменной устанавливается только п р и получении запроса
POST. В переменной CONTENTLENGTH указывается количество байтов данных, со­
держащееся в теле запроса, т.е. CONTENTLENGTH предоставляет ту же информа­
цию, что и поле заголовка C o n t e n t - L e n g t h . Для того чтобы получить информа­
цию, аналогичную значению переменной окружения CONTENTLENGTH, надо вы­
звать S t r i n g . v a l u e O f ( r e q u e s t . g e t C o n t e n t L e n g t h О ) или r e q u e s t . g e t -
H e a d e r ( " C o n t e n t - L e n g t h " ) . Однако на практике многие предпочитают исполь­
зовать метод r e q u e s t . g e t C o n t e n t L e n g t h ( ) , который возвращает значение i n t .
19.8. Аналоги стандартных CGI-переменных 851

CONTENT.TYPE
Переменная окружения CONTENT_TYPE определяет М1МЕ-тип данных, содержа­
щихся в составе запроса. Часто используемые МШЕ-типы перечислены в
табл. 19.1. Информацию, аналогичную CONTENT_TYPE, можно получить, вызвав
метод r e q u e s t . g e t C o n t e n t T y p e ( ) .

DOCUMENT_ROOT
Переменная DOCUMENT_ROOT определяет каталог, соответствующий URL
h t t p : / / h o s t / , где h o s t означает конкретное имя узла. Для получения соответст­
вующего значения используется метод g e t S e r v l e t C o n t e x t () . g e t R e a l -
Path ( " / " ) . Более старые спецификации сервлетов предполагали возможность
вызова r e q u e s t . g e t R e a l P a t h ( " / " ) , однако такой подход уже не поддерживает­
ся. Для отображения произвольного URI (части URL, следующей за адресом узла и
номером порта) в реальный путь на локальной машине можно также воспользо­
ваться методом g e t S e r v l e t C o n t e x t ( ) . g e t R e a l P a t h .

HTTP^XXX.YYY
Переменные окружения с именами в представленном формате позволяют полу­
чать необходимые значения полей HTTP-заголовка. Например, содержимое поля
Cookie доступно посредством переменной НТТР_С00К1Е, полю User-Agent со­
ответствует переменная HTTP__USER__AGENT, значение HTTP_REFERER определяет­
ся содержимым поля R e f e r e r и т.д. Сервлеты могут использовать для этой цели
метод r e q u e s t . g e t H e a d e r либо один и специальных методов, описанных в раз­
деле 19.7.

PATHJNFO
Переменная PATHINFO предоставляет информацию, заданную в URL после адре­
са сервлета и перед данными запроса. Например, для выражения h t t p : / / h o s t /
s e r v l e t / c w p . S o m e S e r v l e t / f oo/bar?baz=quux в состав PATH_INFO будет вклю­
чена последовательность символов / f с о / b a r . Поскольку сервлет, в отличие от
CGI-программ, может непосредственно взаимодействовать с сервером, данные,
содержащиеся в PATH_INFO, для его работы не требуются, однако получить соот­
ветствующее значение можно вызовом метода r e q u e s t . g e t P a t h I n f о.

PATH^TRANSLATED
Переменная окружения PAT Н Т RAN SLATED предоставляет информацию о пути,
отображенную в реальный путь на сервере. Для сервлетов эти данные не нужны,
поскольку для преобразования пути можно вызвать метод g e t S e r v l e t ­
Context () . g e t R e a l P a t h . Подобные преобразования нельзя выполнить в тради­
ционных CGI-программах, поскольку они выполняются независимо от сервера.
Для получения значения данной переменной используется метод r e q u e s t . G e t -
PathTranslated.
852 Глава 19. Java на стороне сервера: сервлеты

QUERY_STRING
В случае запроса GET данная переменная предоставляет строку параметров, при­
соединенную к URL. В сервлетах необходимость в этих данных практически нико­
гда не возникает, так как разработчик может для получения конкретных парамет­
ров воспользоваться методом r e q u e s t . g e t P a r a m e t e r (см. раздел 19.7). Если по
каким-то причинам вам нужны необработанные данные, получить их можно по­
средством метода r e q u e s t . g e t Q u e r y S t r i n g .

REMOTE__ADDR
Данная переменная определяет IP-адрес клиента, от которого поступил запрос.
Адрес представляется в строковом виде (например, "198.137.241.30"). Эти сведе­
ния могут быть также получены посредством метода r e q u e s t . g e t Remote Addr ( ) .

REMOTE_HOST
Переменная REMOTE_HOST содержит полное доменное имя клиента (например,
m y c l i e n t . g o v ) . Если доменное имя не может быть определено, возвращается
IP-адрес. Аналогичные данные можно получить с помощью метода r e q u e s t .
getRemoteHost().

REMOTE^USER
Если в составе запроса был заголовок A u t h o r i z a t i o n , обработанный сервером,
переменная окружения REMOTEUSER содержит данные о пользователе, которые
можно использовать для отслеживания сеансов на защищенных узлах. Данную ин­
формацию можно получить с помощью метода r e q u e s t . g e t R e m o t e U s e r ( ) .

REQUEST^METHOD
Данная переменная определяет тип HTTP-запроса (обычно это GET или POST, но
иногда также встречаются значения HEAD, PUT, DELETE, OPTIONS и TRACE).
В сервлетах информация о типе запроса обрабатывается автоматически и в зави­
симости от используемого типа вызывается метод d o G e t , do P o s t и т.д. Исключе­
нием является запрос HEAD, который автоматически обрабатывается методом
s e r v i c e . Данные, соответствующие значению переменной REQUESTMETHOD,
можно получить с помощью метода r e q u e s t . g e t M e t h o d ( ) .

SCRIPT__NAME
Данная переменная указывает путь к сервлету относительно корневого каталога
сервлетов. Получить эти сведения можно, вызвав метод r e q u e s t . g e t S e r v l e t P a t h .

SERVER_NAME
Переменная окружения SERVER_NAME содержит имя узла, на котором выполняется
сервер. Для получения этих данных используется метод r e q u e s t . g e t S e r v e r N a m e .

SERVER_PORT
В данной переменной содержится номер порта, по которому сервер ожидает об­
ращения. Для того чтобы получить аналогичные данные, надо использовать вызов
19.9. Ответ сервера: коды состояния 853

s t r i n g . v a l u e O f ( r e q u e s t . g e t S e r v e r P o r t ( ) ) , в результате которого будет


возвращен объект S t r i n g . Однако разработчики сервлетов в большинстве случаев
предпочитают вызывать метод r e q u e s t . g e t S e r v e r P o r t ( ) , возвращающий це­
лочисленное значение.

SERVER_PROTOCOL
Переменная SERVER_PROTOCOL сообщает название и версию протокола, указан­
ного в запросе (например, H T T P / 1 . 0 или H T T P / 1 . 1 ) . Эти данные можно получить
также с помощью метода r e q u e s t . g e t P r o t o c o l .

SERVER_SOFTWARE
Данная переменная окружения содержит и н ф о р м а ц и ю о программном обеспече­
нии Web-сервера. В сервлетах получить подобные сведения можно вызовом мето­
да g e t S e r v l e t C o n t e x t ( ) . g e t S e r v e r I n f о .

19.9. Ответ сервера: коды состояния


Ответ Web-сервера на запрос броузера или другого Web-клиента обычно состоит
из строки состояния, нескольких полей заголовка, пустой строки и кода документа.
Пример такого ответа (сокращенный до минимума) имеет следующий вид:
НТТР/1.1 200 ОК
Content-Type: text/plain
Hello World
В строке состояния указываются версия протокола H T T P (в приведенном примере
это Н Т Т Р / 1 . 1 ) , код состояния (в данном случае— 200) и краткое сообщение, описы­
вающее код состояния (в данном примере — ОК). Большинство полей заголовка указы­
вать не обязательно. Исключением является поле C o n t e n t - Т у р е , содержащее MIME-
тип передаваемого документа. Как правило, в состав ответа входит код документа, но
иногда ответ состоит из одного заголовка (включает строку состояния, поля и пустую
строк)^. В качестве примера можно привести ответ на запрос HEAD, в составе которо­
го никогда не передается документ, а также ответы, коды состояния которых указы­
вают на наличие ошибки.
Изменяя содержимое строки состояния и полей заголовка, сервлеты могут выпол­
нять ряд полезных задач. Так, например, сервлет может перенаправить пользователя
на другой узел, указать, что ресурс, передаваемый в составе ответа, является изобра­
жением, файлом в формате Adobe Acrobat или HTML-документом, сообщить пользо­
вателю о том, что для доступа к документу необходим пароль, и т.д. В данном разделе
рассматриваются наиболее важные коды состояния. Поля заголовка ответа будут об­
суждаться в следующем разделе.

Установка кода состояния


Как было сказано выше, в строке состояния HTTP-ответа указаны версия протоко­
ла HTTP, код состояния и текстовое сообщение. Поскольку сообщение полностью
определяется кодом состояния, а версия протокола зависит от используемого серве-
854 Глава 1 9 . Java на с т о р о н е с е р в е р а : сервлеты

ра, все, что может сделать разработчик сервлета, — предусмотреть установку кода со­
стояния. Для этого используется метод s e t S t a t u s класса H t t p S e r v l e t R e s p o n s e .
Компоненты HTTP-ответа расположены в строго определенном порядке. Поэтому,
если ваш ответ содержит специальный код состояния, а документ входит в состав от­
вета, убедитесь в том, что метод s e t S t a t u s вызывается перед передачей содержимо­
го документа посредством P r i n t W r i t e r . Как было сказано в разделе 19.3, сервер не
обязательно сохраняет документ в буфере (спецификация Servlet 2.1 вовсе не преду­
сматривает буферизацию). В связи с этим вы должны либо установить код состояния
до обращения к P r i n t W r i t e r , либо сделать это до того, как содержимое буфера будет
передано броузеру.
М е т о д и к а профессионалов

Устанавливайте код состояния до того, как содержимое документа


будет передано клиенту.

Метод s e t S t a t u s получает в качестве параметра целочисленное значение (код


состояния). Для простоты использования данного метода в классе H t t p S e r v l e t ­
R e s p o n s e объявлен ряд констант. Имя каждой константы создается на основании со­
общения, поясняющего данный код в строке состояния HTTP-ответа. Сообщение
преобразуется в верхний регистр, предваряется префиксом SC, а пробелы заменяют­
ся символами подчеркивания. Так, если коду 404 соответствует сообщение "Not
Found", то константа, определенная в H t t p S e r v l e t R e s p o n s e , имеет вид
SCNOTFOUND. В версии Servlet 2.1 существовало три исключения из данного прави­
ла. Имя константы для кода 302 было создано в соответствии с сообщением H T T P 1.0
(Moved Temporarily), а константы для кодов 307 (Temporary Redirect) и 416
(Requested Range Not Satisfiable) вовсе отсутствовали. В версии 2,2 спецификации бы­
ла определена константа для кода 416, но несоответствие кодов 307 и 302 осталось.
Несмотря на наличие универсального метода для установки кода состояния, в
H t t p S e r v l e t R e s p o n s e определены два специальных метода, описанных ниже. Если
при выполнении s e t S t a t u s исключения не возникают, то оба приведенных ниже
метода могут генерировать исключение l O E x c e p t i o n .
• public void sendError(int code, String message)
Метод s e n d E r r o r передает клиенту код состояния (обычно это код 404) и крат­
кое сообщение, которое автоматически оформляется в виде HTML-документа.
• public void sendRedirect(String url)
Метод s e n d R e d i r e c t генерирует ответ с кодом состояния 302 и полем
L o c a t i o n , содержащим URL нового документа. В соответствии с версией 2.1
спецификации в качестве параметра должен быть задан абсолютный URL. Вер­
сия 2.2 позволяет задавать как абсолютные, так и относительные URL. Перед
заполнением поля L o c a t i o n система автоматически преобразует относитель­
ный URL в абсолютный.
Установка кода состояния не исключает генерации HTML-документа. Например,
несмотря на то, что большинство серверов автоматически генерирует для ответа с
кодом 404 простое сообщение "File Not Found", автор сервлета может предусмотреть
в составе ответа дополнительную информацию.
19.9. Ответ сервера: коды состояния 855

Коды состояния HTTP 7.1


в данном разделе мы рассмотрим наиболее важные коды состояния, применяемые
при организации взаимодействия сервлетов с клиентами по протоколу H T T P 1.1. Ка­
ждому коду соответствует стандартное сообщение. Зная эти коды, вы сможете значи­
тельно повысить возможности создаваемых вами сервлетов.
Полный текст спецификации H T T P 1.1 приведен в документе RFC 2616. Ссылки на
электронные версии RFC вы найдете по адресу h t t p : / / w w w . r f c - e d i t o r . o r g / . Не
следует забывать, что некоторые броузеры могут взаимодействовать с серверами
лишь по протоколу H T T P 1.0, поэтому, перед тем, как передавать код, специфический
для H T T P 1.1, следует убедиться в том, что броузер поддерживает данную версию
протокола. Сделать это можно, вызвав метод r e q u e s t . g e t R e q u e s t P r o t o c o l .
Коды состояния разделяются на пять категорий.

• 100-199
Коды, в который первая из трех ц и ф р равна 1, указывают на то, что, получив
ответ, клиент должен выполнить некоторые действия.
• 200 - 299
Коды, начинающиеся с ц и ф р ы 2, сообщают о том, что запрос был обработан
успешно.
• 300-399
Коды в данном диапазоне означают, что файл, указанный в запросе, был пере­
мещен. О б ы ч н о при этом в состав ответа включается поле L o c a t i o n , содер­
жащее новый адрес ресурса.
• 400-499
Коды, начинающиеся с цифры 4, соответствуют ошибкам, допущенным клиентом.
• 500 - 599
Коды в данном диапазоне указывают на ошибку сервера.

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


H t t p S e r v l e t R e s p o n s e . В сервлетах эти константы применяются при установке ко­
дов состояния. Например, вместо r e s p o n s e . s e t S t a t u s (204) обычно используют
вызов r e s p o n s e . s e t S t a t u s ( r e s p o n s e . SC_NO_CONTENT). Применение констант
уменьшает вероятность ошибки и делает код более удобочитаемым. Заметьте, что
текстовые сообщения в разных серверах могут различаться, но клиенты учитывают
только числовой код. Например, вместо Н Т Т Р / 1 . 1 200 ОК клиент может получить в
строке о т в е т а Н Т Т Р / 1 . 1 200 Document F o l l o w s .

100 (Continue)
Если сервер получает в составе запроса поле E x p e c t , содержащее 1 0 0 - c o n t i n u e ,
это означает, что клиент запрашивает разрешение передать в составе следующего
запроса код документа. Если сервер отправит ответ с кодом состояния 100
(SCCONTINUE), то клиент может продолжать передачу данных. Код 417
( E x p e c t a t i o n F a i l e d ) сообщает о том, что броузер не может принять'документ.
Код состояния 100 был введен в HTTP 1.1.
856 Глава 19. Java на стороне сервера: сервлеты

200 ( О К )
Код 200 (SCOK) говорит о том, что обработка запроса окончилась успешно. В со­
ставе ответа передается документ, указанный в запросе. Данный код устанавлива­
ется для сервлетов по умолчанию. Если вы не вызовете метод s e t S t a t u s , сервер
установит код 200.

201 (Created)
Код состояния 201 (SC_CREATED) означает, что сервер в ответ на запрос создал
новый документ. URL этого документа помещается в поле заголовка L o c a t i o n .

202 (Accepted)
Код 202 (SCACCEPTED) информирует клиента о том, что запрос был принят, но
. обработка его еще не закончилась.

204 ( N o Content)
Код состояния 204 (SC_NO_CONTENT) сообщает броузеру, что тот должен отобра­
жать предыдущий документ, поскольку новый документ недоступен. Этот код по­
лезен в том случае, когда пользователь периодически перезагружает Web-страницу
с помощью кнопки Reload и на каком-то этапе сервлет обнаруживает, что очеред­
ной вариант документа полностью повторяет предыдущий.

205 (Reset Content)


Значение 205 (SC RESETCONTENT) указывает на то, что новый документ отсутст­
вует, но броузер должен вернуть внешний вид предыдущего документа в исходное
состояние. Этот код используется для того, чтобы принудительно осуществлять
сброс содержимого элементов ф о р м ы . Данный код введен в H T T P 1.1.

301 (Moved Permanently)


Код состояния 301 (SC_MOVED_PERMANENTLY) сообщает о том, что запрашиваемый
документ находится по другому адресу. Новый URL документа указывается в поле за­
головка L o c a t i o n . Броузер должен автоматически обратиться по этому URL.

302 (Found)
Данное значение аналогично коду 301 за исключением того, что URL, указанный в
поле L o c a t i o n , должен интерпретироваться как временный адрес ресурса. На
практике большинство броузеров обрабатывает коды 301 и 302 одинаково. За­
метьте, что в HTTP 1.0 данному коду соответствовало сообщение Moved Tempo­
r a r i l y , а в H T T P 1.1 — сообщение Found. Вопреки ожиданиям, константа, объяв­
ленная в классе H t t p S e r v l e t R e s p o n s e , называется не SC_FOUND, а
SC_MOVED_TEMPORARILY.
На заметку

Имя константы, представляющей код 302, не SC_FOUND, а SC^MOV-


ED TEMPORARILY,
19.9. Ответ сервера: коды состояния 857

Код состояния 302 полезен, поскольку броузеры автоматически следуют ссылке, ука­
занной в поле L o c a t i o n . Кроме того, для поддержки данного кода предусмотрен
специальный метод sendRedirect. Использование response. sendRedi-
rect(url) предпочтительнее, чем обращение к методу r e s p o n s e . s e t -
S t a t u s ( r e s p o n s e . SC_MOVED_TEMPORARILY) с последующим вызовом r e s p o n s e .
s e t H e a d e r ( " L o c a t i o n " , u r l ) . Во-первых, код сервлета упрощается, во-вторых,
]1ЛЯ броузеров, не поддерживающих перенаправление, сервлет с помощью
s e n d R e d i r e c t автоматически создает страницу, содержащую ссылку на ресурс. И,
наконец, при работе с версией 2.2 (используемой в J2EE) метод s e n d R e d i r e c t под­
держивает относительные URL и автоматически преобразовывает их в абсолютные.
Предполагается, что броузеры автоматически следуют по указанному адресу только в
случае запроса GET. Дополнительная информация приведена в описании кода 307.

303 (See Other)


Код 303 (SC_SEE_OTHER) действует аналогично кодам 301 и 302, за исключением
одной особенности. Если в исходном запросе был использован метод POST, то ре­
сурс, адрес которого указан в поле заголовка L o c a t i o n , должен быть получен с
помощью метода GET. Данный код введен в H T T P 1.1.

304 (Not Modified)


Если документ уже находится в кэше, клиент может передать серверу условный за­
прос, содержащий поле заголовка I f - M o d i f i e d - S i n c e . Это означает, что доку­
мент должен быть передан только в том случае, если он был модифицирован поз­
же даты, указанной в поле If-Modif ied-Since. Значение 304
(SCNOTMODIFIED) сообщает броузеру о том, что версия в кэше совпадает с вер­
сией на сервере и клиент может использовать ее. Если документ был модифициро­
ван позже указанной даты, сервер должен передать этот документ и указать в стро­
ке состояния код 200. Создавая сервлет, не следует непосредственно передавать
код 304, вместо этого надо реализовать в сервлете метод g e t L a s t M o d i f i e d и пре­
доставить методу s e r v i c e самостоятельно обрабатывать условные запросы.

307 (Temporary Redirect)


Броузер обрабатывает коды 307 и 302 по одним и тем же правилам. Код 307 был до­
бавлен в HTTP 1.1 потому, что многие броузеры ошибочно обрабатывали код 302,
следуя по новому адресу даже в том случае, если в исходном запросе был использован
метод POST. Перенаправление запроса POST должно происходить только при полу­
чении кода 303. Новый код состояния был призван упорядочить действия броузеров:
при получение кода 303 должны перенаправляться как запросы GET, так и запросы
POST, а при получении кода 307 перенаправляется GET, но не POST. В классе
H t t p S e r v l e t R e s p o n s e не предусмотрена константа, соответствующая данному
коду, поэтому при вызове метода s e t S t a t u s надо указывать число 307.

400 (Bad R e q u e s t )
Код состояния 400 (SC_BAD_REQUEST) сообщает о том, что запрос клиента был со­
ставлен некорректно и содержал синтаксические ошибки.
858 Глава 19. Java на стороне сервера: сервлеты

401 ( U n a u t h o r i z e d )
Значение 401 (SC_UNAUTHORIZED) указывает на то, что клиент старается получить
доступ к документу, защищенному паролем, не указав требуемые идентификаци­
онные данные в поле заголовка A u t h o r i z a t i o n . Ответ должен содержать поле
WWW-Authenticate.

403 ( F o r b i d d e n )
Код состояния 403 (SC_FORBIDDEN) сообщает, что, независимо от идентификаци­
онных данных, сервер отказывается предоставить ресурс. Часто такой ответ гене­
рируется в тех случаях, когда права доступа к файлу или каталогу на сервере уста­
новлены неправильно.

404 ( N o t F o u n d )
Код 404 (SCNOTFOUND) информирует клиента о том, что ресурс, URL которого
был указан в составе запроса, отсутствует. Данный код встречается настолько час­
то, что для его поддержки в классе H t t p S e r v l e t R e s p o n s e был предусмотрен
специальный метод s e n d E r r o r . В качестве параметра этому методу передается
текстовое сообщение. Преимущество метода s e n d E r r o r перед методом
s e t S t a t u s состоит в том, что при выполнении s e n d E r r o r сервер автоматически
генерирует страницу с сообщением об ошибке. По умолчанию Internet Explorer 5
игнорирует сообщение об ошибке и отображает свою страницу, хотя это и проти­
воречит спецификации HTTP. Для того чтобы отменить эту установку, надо вы­
брать пункт меню Tools (Сервис), перейти в меню Internet Options (Свойства обо­
зревателя), выбрать вкладку Advanced (Дополнительно) и сбросить флажок опции
Show friendly HTTP error m e s s a g e s (Выводить подробные сообщения об ошибках
HTTP). К сожалению, большинство пользователей ничего не знают об этой уста­
новке Internet Explorer 5, поэтому сообщения об ошибках, генерируемые на сто­
роне сервера, для них недоступны. Другие популярные броузеры и Internet
Explorer 4 нормально отображают подобные сообщения.

Внимание!
По умолчанию internet Explorer игнорирует сообщения об ошибках,
генерируемые сервером.

405 ( M e t h o d N o t Allowed)
Код 405 (SC_METHOD_NOT_ALLOWED) указывает на то, что конкретный метод (GET,
POST, HEAD, PUT, DELETE и т.д.) недопустим для данного ресурса. Этот код был
введен в H T T P 1.1.

410 ( G o n e )
Код состояния 410 (SCGONE) сообщает клиенту о том, что запрашиваемый доку­
мент удален и адрес для перенаправления запроса неизвестен. Код 410 отличается
от кода 404 тем, что он явно указывает, что документ был удален, а код 404 сооб­
щает, что ресурс не найден и п р и ч и н ы этого неизвестны. Данный код состояния
был введен в H T T P 1.1.
19.9. Ответ сервера: коды состояния 859

411 (Length R e q u i r e d )
Код 411 (SC_LENGTH_REQUIRED) указывает на то, что сервер не может обработать
запрос (обычно это запрос POST с присоединенным документом), поскольку кли­
ент не включил в него поле C o n t e n t - L e n g t h , сообщающее об объеме данных, пе­
редаваемых на сервер. Д а н н ы й код был введен в H T T P 1.1.

413 (Request Entity T o o Large)


Код 41S (SC_REQUEST_ENTITy_TOO__LARGE) информирует клиента о том, что объ­
ем запрашиваемого документа больше, чем сервер может обработать. Если сервер
считает, что сможет обслужить запрос позже, он должен включить в состав ответа
поле заголовка R e t r y - A f t e r . Данный код был введен в H T T P 1.1.

414 (Request U R I T o o L o n g )
Код состояния 414 ( S C R E Q U E S T U R I T O O L O N G ) сообщает, что длина URI в со­
ставе запроса слишком велика. URI — это часть URL, находящаяся после адреса уз­
ла и номера порта. Н а п р и м е р , в URL h t t p : / / w w w . y 2 k - d i s a s t e r . c o m : 8 0 8 0 /
w e / l o o k / s i l l y / n o w / последовательность символов / w e / l o o k / s i l l y / n o w / со­
ставляет URI. Данный код был введен в H T T P 1.1.

415 ( U n s u p p o r t e d M e d i a Type)
Значение 415 (SC_UNSUPPORTED_MEDIA_TYPE) указывает на то, что тип докумен­
та, переданного в составе запроса, неизвестен серверу и сервер не знает, как обра­
ботать его. Данный код был введен в H T T P 1.1.

417 (Expectation Failed)


Если сервер получил запрос с полем заголовка E x p e c t , содержащим 1 0 0 -
c o n t i n u e , это означает, что клиент спрашивает, может ли он передать серверу
документ в последующем запросе. Сервер должен либо сформировать ответ с ко­
дом состояния 417, чтобы сообщить броузеру о том, что он не в состоянии обрабо­
тать документ, либо сформировать ответ с кодом 100 (SC CONTINUE), оповещая
броузер, что тот может ф о р м и р о в а т ь следующий запрос. Данный код был введен в
H T T P 1.1.

500 (Internal Server Error)


Код 500 (SC_INTERNAL_SERVER_ERROR) представляет собой универсальное сооб­
щение типа "ошибка на сервере". Часто ответ с таким кодом передается, если при
выполнении CGI-программы или сервлета произошел сбой и они не смогли сфор­
мировать корректный заголовок.

501 ( N o t I m p l e m e n t e d )
Код 501 (SC_NOT_IMPLEMENTED) оповещает клиента о том, что функции, необходи­
мые для обслуживания запроса, на сервере не реализованы. Такой ответ клиент мо­
жет получить в ответ на запрос PUT, если сервер не поддерживает данный метод.
860 Глава 19. Java на стороне сервера: сервлеты

503 (Service Unavailable)


Код состояния 503 (SC_SERVICE_UNAVAILABLE) указывает на то, что сервер не
может обработать запрос из-за чрезмерной нагрузки либо вследствие выполнения
профилактических работ. Так, например, сервлет может вернуть ответ с таким ко­
дом, если возможности по созданию новых потоков исчерпаны или если пул со­
единений с базой данных полностью задействован. О б ы ч н о в таких случаях сервер
передает в составе ответа поле заголовка R e t r y - A f t e r , указывающее на то, что
клиент должен повторить запрос позже.

505 ( H T T P V e r s i o n N o t S u p p o r t e d )
Код 505 (SC_HTTP_VERSION_NOT_SUPPORTED) означает, что сервер не поддержи­
вает версию H T T P , указанную в запросе. Данный код был введен в l i T T P 1.1.

Интерфейс к различным поисковым серверам


в листинге 19.14 представлен код, который демонстрирует использование часто
встречающихся кодов состояния: 302 и 404. Код 302 устанавливается с помощью ме­
тода s e n d R e d i r e c t класса H t t p S e r v l e t R e s p o n s e , а для установки кода 404 исполь­
зуется метод s e n d E r r o r .
В приведенном примере сначала отображается страница, содержащая HTML-
форму (рис. 19.9 и исходный код в листинге 19.16). С помощью ф о р м ы пользователь
может задать ключевые слова для поиска, число результатов на странице и исполь­
зуемый поисковый сервер. После того как данные ф о р м ы будупг переданы на сервер,
сервлет извлекает т р и параметра, создает URL, содержащий запрос в форме, специ­
фической для выбранного поискового сервера (класс S e a r c h S p e c в листинге 19.15),
и перенаправляет клиента по этому URL (рис. 19.10). Если пользователь не выбрал
поисковый сервер или не указал ключевые слова, формируется Web-страница, сооб­
щающая о допущенной ошибке (см. описание кода состояния 404).

Листинг 1 9 . 1 4 . S e a r c h E n g i n e s . J a v a

р а с к а д е cwp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;
/** Сервлету передаются ключевые слова для поиска, число
* результатов на странице и имя поискового сервера.
* Данный сервлет иллюстрирует использование заголовка
* ответа. Если сервлету известен поисковый сервер, он
* генерирует ответ 302 (посредством sendRedirect).
* В противном случае клиенту передается ответ 4 04
* (посредством sendError).
*/
public class SearchEngines extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
19.9. Ответ сервера: коды состояния 861

throws ServletException, lOException {


String searchString = request.getParameter("searchString") ;
if ( (searchString == null) | i
(searchString.length 0 == 0)) {
reportProblem(response, "Missing search string.");
return;
}
// URLEncoder заменяет пробелы знаками "+", a символы,
// отличные от латинских букв и цифр, — последовательностью
// "%XY", где XY - двузначное шестнадцатеричное число,
// определяющее код символа. Броузеры всегда
// кодируют данные, передаваемые на сервер, а метод
// getParameter автоматически декодирует их. Поскольу
// нам надо передать данные другому серверу, их надо
// снова закодировать.
searchString = URLEncoder.encode(searchString);
String numResults = request.getParameter("numResults");
if ((numResults == null) ||
(numResults.equals("0")) ||
(numResults.length 0 = = 0 ) ) {
numResults = "10";
}
String searchEngine =
request.getParameter("searchEngine");
if (searchEngine == null) {
reportProblem(response, "Missing search engine name.");
return;
}
SearchSpec[] commonSpecs = SearchSpec.getCommonSpecs();
for(int i=0; i<commonSpecs.length; i++) {
SearchSpec searchSpec = commonSpecs[i];
if (searchSpec.getName().equals(searchEngine)) {
String url =
searchSpec.makeURL(searchString, numResults);
response.sendRedirect(url);
return;
}
}
reportProblem(response, "Unrecognized search engine.");

private void reportProblem(HttpServletResponse response.


String message)
throws lOException {
response . sendError (response . SC__NOT_FOUND,
"<H2>" + message + "</H2>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
doGet(request, response);
862 Глава 19. Java на стороне сервера: сервлеты

Листинг 1 9 . 1 5 . S e a r c h S p e c . J a v a

p a c k a g e cwp;

Z'^* Класс, предназначенный для создания строки запроса


* к конкретному серверу.

public class SearchSpec {


private String name, baseURL, numResultsSuffix;

private static SearchSpec[] commonSpecs =


{ new SearchSpec("google",
"http:ZZwww.google.comZsearch?q=",
"&num="),
new SearchSpec("infoseek",
"http:ZZinfoseek.go.comZTitles?qt=",
"&nh="),
new SearchSpec("lycos",
"http:ZZlycospro.lycos.comZcgi-binZ" +
"pursuit?query=",
"&maxhits="),
new SearchSpec("hotbot",
"http:ZZwww.hotbot.comZ?MT=",
"&DC=")
};

•public SearchSpec(String name.


String baseURL,
String numResultsSuffix) {
this.name = name;
this.baseURL = baseURL;
this.numResultsSuffix = numResultsSuffix;
}
public String makeURL(String searchString,
String numResults) {
return(baseURL + searchString +
numResultsSuffix + numResults);
}
public String getName() {
return(name);
}
public static SearchSpec[] getCommonSpecs() {
return(commonSpecs) ;
}
}
19.9. Ответ сервера: коды состояния 863

j;^»A!ijj!i;ii|jf|MJi|.H.iiu/^i.!iifira!ff^^

'.r-T^jTa^a й-м^Л'^-j'
AiJdiess \fi] h(tp //localhost/cwp/Seafd-£ngines html ^J ^ ^^^

3
Searching the Web
Search String: |serviets J S P book
Results to Show Per Page fTF~
<? Google I <" bifoseek | r L/COS | r HotBot
Se«rch I

J
^ Done 1 ^ local rtidfiet

Рис. 19.9. Интерфейс к сервлету SearchEngines. Исходный HTML-код приведен в лис­


тинге 19.16

; А Й * < » * | ^ ht»p //ww*M google comAearch'?q=servle(s-KJSP+book8<num=10 j j fv>So

Advanced Search Language. Display. & Filtenno Options Search Tips

Gougle Isetvlets JSP book Google S«erch I'm Feelmg Lucky

:?'«о.Н' • / ; I' 1П0М 1'/. •: 2,580* vservlets JSP book .-^'Г' i L-^-0.68 ^.7.'.- ^
"^-i'"'';'"^ Computers > Programming > Languages > Java > Runtime Environments > Sen/lets

Core S e r v l e t s and JavaServer Pages (JSP): Book Table of


...contents of Core Servlets and JavaServer Pages (JSP) in HTML...
...CHAPTER 11 The JSP page Directive. Structuring Generated Servlets...
vvWiAf.coreseivlels.corn/rabie-Oi-Coritentb himi - 20k- Cs.kMfJ - ШВ1:К£:Ш!к^

Core S e r v l e t s and JavaServer Pages


...and integrating servlets and JSP) The book includes...
GosciVptiOi:- Home page for new seivlet/JSP book from Sun Microsystems Press and Prentice Hall.
Covers servlets

zl
iigj Ф intefnet

Рис. 19.10. Результаты обработки сервлетом данных, представленных на рис. 19.9


864 Глава 19. Java на стороне сервера: сервлеты

Листинг 19.16,SearchEngines.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Searching the Web</TITLE>
</HEAD>

<BODY BGC0L0R="#FDF5E6">
<H1 ALIGN="CENTER">Searching the Web</Hl>

<FORM ACTION="/servlet/cwp.SearchEngines">
<CENTER>
Search String:
<INPUT TYPE="TEXT" NAME="searchString"><BR>
Results to Show Per Page:
<INPUT TYPE="TEXT" NAME="numResults"
VALUE=10 SIZE=3><BR>
<INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="google">
Google I
<INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="infoseek">
Infoseek |
<INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="lycos">
Lycos I
<INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="hotbot">
HotBot
<BR>
<INPUT TYPE="SUBMIT" VALUE="Search">
</CENTER>
</FORM>

</BODY>
</HTML>

19.10. Ответ сервлета: поля заголовка


Как было сказано в предыдущем разделе, ответ Web-сервера состоит из строки со­
стояния, одного или нескольких полей заголовка, пустой строки и содержимого до­
кумента. Чтобы обеспечить максимальную эффективность сервлета, надо не только
уметь генерировать документ, но и знать коды состояния и поля заголовка.
Установка полей заголовка часто сопровождает установку кода состояния. Напри­
мер, коды, соответствующие перемещению документов (300-307), предполагают на­
личие в составе ответа поля L o c a t i o n , а коду 401 ( U n a u t h o r i z e d ) сопутствует поле
w w w - A u t h e n t i c a t e . Поля заголовка приходится устанавливать не только тогда, когда
используются "экзотические" коды состояния. С их помощью задаются записи cookie,
указывается дата модификации документа, передается указание броузеру перезагру-
19.10. Ответ сервлета: поля заголовка 865

зить Web-страницу через заданный интервал, указывается размер документа, переда­


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

Установка полей заголовка


Для установки полей заголовка используется метод s e t H e a d e r класса H t t p -
S e r v l e t R e s p o n s e . Данному методу передаются два строковых параметра: один из
них задает имя поля, а другой — значение. Как и в случае с кодами ответов, значения
полей заголовка надо устанавливать перед передачей содержимого документа.
В дополнение к универсальному методу s e t H e a d e r , в классе H t t p S e r v l e t -
R e s p o n s e предусмотрены также два специальных метода для установки полей, со­
держащих даты и целочисленные значения.
• setDateHeader(String header, long milliseconds)
Данный метод избавляет разработчика от необходимости преобразовывать дату,
представленную в миллисекундах, прошедших с начала 1970 г. (значения в таком
формате возвращают методы S y s t e m . c u r r e n t T i m e M i l l i s , D a t e . g e t T i m e и
Calendar. getTimelnMillis).
• setIntHeader(String header, int header Value)
Данный метод избавляет от необходимости преобразования данных i n t в объ­
ект S t r i n g перед включением их в состав поля.
H T T P допускает наличие в составе заголовка нескольких полей с одинаковыми
именами, поэтому часто бывает удобнее включить еще одно поле, чем искать сущест­
вующее и заменять его новым. Так, например, часто можно встретить в одном заго­
ловке несколько полей A c c e p t или S e t - C o o k i e , которые определяют различные
MIME-ТИПЫ и различные записи c o o k i e . Методы s e t H e a d e r , s e t D a t e H e a d e r и
s e t l n t H e a d e r , соответствующие спецификации SeiTlet 2.1, всегда добавляют новые
поля заголовка, в то же время в данной версии не были предусмотрены способы уда­
ления полей заголовков, установленных ранее. Согласно спецификации Sei'vlet 2.2,
методы s e t H e a d e r , s e t D a t e H e a d e r и s e t l n t H e a d e r заменяют существующее поле
с тем же именем, а чтобы поле заголовка было создано независимо от того, имеется
ли поле с совпадающим именем, используются методы a d d H e a d e r , a d d D a t e H e a d e r и
a d d l n t H e a d e r . Если вам необходимо знать, установлено ли конкретное поле, вы мо­
жете проверить это с помощью метода c o n t a i n s H e a d e r .
И, наконец, в методе H t t p S e r v l e t R e s p o n s e также предусмотрены специальные
методы для установки часто используемых полей. Эти методы перечислены ниже.

• setContentType
Данный метод устанавливает поле заголовка C o n t e n t - T y p e и используется
большинством сервлетов.
• setContentLength
Данный метод устанавливает поле заголовка C o n t e n t - L e n g t h и используется в том
случае, если броузер поддерживает постоянное (keep-alive) HTTP-соединение.
866 Глава 19. Java на стороне сервера: сервлеты

• addCookie
Метод addCookie включает в состав заголовка поле S e t - C o o k i e , содержащее
запись cookie. Метод s e t Cookie отсутствует, поскольку наличие в одном заго­
ловке нескольких полей cookie— вполне нормальное явление. Механизм
cookie будет рассматриваться в разделе 19.11.
• sendRedirect
Как было сказано в предыдущем разделе, метод s e n d R e d i r e c t , помимо уста­
новки кода состояния 302, включает также в состав заголовка поле L o c a t i o n .
Пример использования данного метода приведен в листинге 19.14.

Поля заголовка в составе ответа сервера


Ниже описаны наиболее часто используемые поля заголовка, определенные в спе­
цификации HTTP 1.1. Понимая назначение этих полей, вы сможете повысить эффек­
тивность создаваемых вами сервлетов.
Дополнительную информацию о рассматриваемых полях заголовка вы найдете в
документе RFC 2616, где представлена спецификация HTTP 1.1. Ссылки на электрон­
ные копии RFC находятся по адресу h t t p : / / w w w . r f c - e d i t o r . o r g / . Имена полей
заголовков не зависят от регистра символов, однако принято начинать каждое слово
имени с прописной буквы.
Используя в сервлете поля заголовков, специфические для HTTP 1.1, соблюдайте
осторожность. Если ваш сервлет предназначен для работы в Internet, помните, что
многие броузеры поддерживают только HTTP 1.0. Применять поля HTTP 1.1 имеет
смысл только в том случае, если создаваемый вами сервлет будет использоваться в се­
ти intranet. Перед тем как включать в состав заголовка поле HTTP 1.1, имеет смысл
проверить с помощью метода r e q u e s t . g e t R e q u e s t P r o t o c o l , какая версия HTTP
поддерживается клиент-программой.

Allow
Данное поле заголовка указывает методы запроса, поддерживаемые сервером. Это
поле обязательно должно входить в состав ответа 405 (Method Not Allowed). По
умолчанию метод s e r v i c e автоматически генерирует поле Allow для запросов
OPTION.

Cache-Control
Поле C a c h e - C o n t r o l сообщает броузеру или другой клиент-программе условия,
при которых документ, переданный в составе ответа, может быть помещен в кэш.
В этом поле могут содержаться следующие значения.
• p u b l i c . Документ может быть помещен в кэш, даже если общепринятые пра­
вила запрещают делать это (например, если документ защищен паролем).
• p r i v a t e . Документ предназначен для одного пользователя и может содер­
жаться только в личном (неразделяемом) кэше.
• п о - c a c h e . Документ не должен помещаться в кэш. Сервер также может за­
дать выражение n o - c a c h e = " h e a d e r l , h e a d e r 2 , . . . ,headerN", в котором
19.10. Ответ сервлета: поля заголовка 867

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


будет использоваться при последующих запросах. Как правило, броузеры не
кэшируют документы, которые были получены в результате запросов, со­
держащих данные формы. Однако если сервлет при каждом вызове генери­
рует новые данные, желательно указать, что информация не должна кэши-
роваться, независимо от способа обращения в сервлету. В броузерах, ис­
пользовавшихся ранее, для этой цели применялось поле Pragma, поэтому
целесообразно включать в состав заголовка оба поля.
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
• n o - S t o r e . Документ не должен помещаться в кэш и даже временно сохра­
няться на диске. Данное значение поля предотвращает создание копий
конфиденциальных данных.
• m u s t - r e v a l i d a t e . Клиент должен каждый раз проверять документ на ис­
ходном узле (а не только на proxy-сервере).
• p r o x y - r e v a l i d a t e . Данное значение выполняет те же действия, что и
m u s t - r e v a l i d a t e , за исключением того, что оно применяется только к
разделяемому кэшу.
• max-age=xxx. Документ считается устаревшим по прошествии ххх секунд.
Это хорошая альтернатива полю заголовка E x p i r e s , но использовать ее
можно только при работе с клиентами, поддерживающими HTTP 1.1. Если в
одном заголовке содержатся max-age и E x p i r e s , предпочтение отдается
max-age.
• s-max-age=xxx В разделяемом кэше документ должен считаться устарев­
шим по прошествии ххх секунд.
Поле заголовка C a c h e - C o n t r o l введено в HTTP 1.1.

Connection
Если в поле C o n n e c t i o n указано значение c l o s e , броузер не должен использовать
постоянное соединение. По умолчанию клиенты, поддерживающие протокол
HTTP 1.1, используют постоянное соединение. Чтобы постоянное соединение со­
хранялось, для клиентов HTTP 1.0 должно быть указано поле C o n n e c t i o n :
k e e p - a l i v e , а для клиентов HTTP 1.1 — не указано C o n n e c t i o n : c l o s e . При на­
личии постоянного соединения в составе заголовка ответа обязательно должно
присутствовать поле C o n t e n t - L e n g t h .
/
Content-Encoding
Данное поле сообщает о том, каким преобразованиям подвергалось содержимое
документа перед передачей. После получения документа броузер должен выпол­
нить обратное преобразование. Если документ был сжат посредством g z i p , при
передаче такого документа экономится много времени. Пример передачи сжатого
документа приведен в разделе 19.7.
868 Глава 19. Java на стороне сервера: сервлеты

Content-Language
Поле C o n t e n t - L a n g u a g e определяет язык документа. Значением этого поля
должно быть стандартное двухбуквенное сокращение, например e n , e n - u s , da и
т.д. Дополнительную информацию вы найдете в RFC 1766.

Content-Length
Данное поле заголовка указывает число байт в документе, содержащемся в составе
ответа. Это поле необходимо только в том случае, если броузер поддерживает по­
стоянное HTTP-соединение. Чтобы определить, может ли броузер использовать по­
стоянное соединение, надо проверить значение поля заголовка C o n n e c t i o n . Если
вы хотите воспользоваться преимуществами постоянного соединения, вам надо за­
писать документ в B y t e A r r a y O u t p u t S t r e a m , определить размер документа, устано­
вить соответствующее значение поля C o n t e n t - L e n g t h , вызвав для этого метод
r e s p o n s e . s e t C o n t e n t L e n g t h , а затем передать содержимое документа посредст­
вом в ы з о в а b y t e A r r a y S t r e a m . w r i t e T o ( r e s p o n s e . g e t O u t p u t S t r e a m ( ) ).

Content-Type
Поле C o n t e n t - T y p e содержит MIME-тип документа, передаваемого в составе от­
вета. Данное поле приходится устанавливать настолько часто, что в классе
H t t p S e r v l e t R e s p o n s e для этого предусмотрен специальный метод s e t C o n -
t e n t T y p e . Зарегистрированные MIME-типы задаются в формате шип/подтип, а
незарегистрированные— в формате тип/х-подтип. По умолчанию для сервлетов
принимается MIME-тип t e x t / p l a i n , однако сервлет может явно задать тип
t e x t / h t m l либо другой требуемый тип.
Часто используемые MIME-типы перечислены в табл. 19.1.
Подробно MIME-типы описаны в документах RFC 1521 и RFC 1522 (ссылки на
электронные версии вы найдете по адресу h t t p : / / w w w . r f c - e d i t o r . o r g / ) . По­
скольку новые MIME-типы постоянно регистрируются, существуют динамически
обновляемые списки типов. О ф и ц и а л ь н о зарегистрированные MIME-типы пере­
числены в документе h t t p : / / w w w . i s i . e d u / i n - n o t e s / i a n a / a s s i g n m e n t s / -
m e d i a ^ t y p e s / m e d i a - t y p e s . И н ф о р м а ц и ю об используемых незарегистрирован­
ных типах можно найти по адресу h t t p : / / w w w . l t s w . s e / k n b a s e / i n t e r n e t / -
mime.htp.

Таблица 1 9 . 1 . Часто используемые MIME-типы


Тип Описание
application/msword Документ Microsoft Word
application/octet-stream Нераспознаваемые или двоичные данные
application/pdf Файл Acrobat ( . p d f )
application/postscript Файл PostScript
application/vnd. lotus-notes Файл Lotus Notes
application/vnd.ms-excel Электронные таблицы Excel
19.10. Ответ сервлета: поля заголовка 869

Окончание табл. 19.1


application/vnd.ms-powerpoint Документ PowerPoint
application/x-gzip Архив Gzip
application/x-java-archive JAR-файл
application/х-jаva­ Сериал изованный Java-объект
se rial i zed-object
application/x-java-vm Байтовый код Java (файл . c l a s s )
application/zip Zip-архив
audio/basic Аудиофайл в формате . а и или . s n d
audio/x-aiff Аудиофайл AIFF
audio/x-wav Аудиофайл Microsoft Windows
audio/midi Аудиофайл MIDI
text/ess Каскадный лист стилей HTML
text/html HTML-документ
text/plain Текстовый докухмент
image/gif / Изображение в формате GIF
image/jpeg Изображение в формате JPEG
image/png Изображение в формате PNG
image/tiff Изображение в формате TIFF
image/x-xbitmap Изображение в формате X Windows
(битовая карта)
video/mpeg Видеоклип MPEG
video/quicktime Видеоклип QuickTime

Expires
Данное поле задает время, по истечении которого документ считается устаревшим
и его версия, содержащаяся в кэше, не должна использоваться. Сервлет может
применять это поле при передаче броузеру документов, содержимое которых под­
вергается частым изменениям. Например, следующий фрагмент кода включает в
состав заголовка поле, сообщающее о том, что документ, полученный броузером,
остается действительным в течение 10 минут.
long currentTime = System.currentTimeMillis ( ) ;
l o n g t e n M i n u t e s = 1 0 * 6 0 * 1 0 0 0 ; / / Значение в миллисекундах
response.setDateHeader("Expires",
currentTime + tenMinutes);
Аналогичные действия выполняет значение m a x - a g e поля C a c h e - C o n t r o l .
870 Глава 19. Java на стороне сервера: сервлеты

Last-Modified
В поле L a s t - M o d i f i e d указывается дата последней модификации документа. Если
броузер включает в состав запроса поле заголовка I f - M o d i f i e d - S i n c e , он фор­
мирует тем самым условный запрос. Документ передается в ответ на этот запрос,
только если дата, указанная в Last-Modif i e d , оказывается более поздней по
сравнению со значением If-Modif i e d - S i n c e . В противном случае сервер фор­
мирует ответ с кодом состояния 304 (Not Modified) и клиент использует копию
документа, содержащуюся в кэше. Если вы собираетесь явно устанавливать данное
поле, используйте для этого метод s e t D a t e H e a d e r . Однако в большинстве случаев
устанавливать значение данного поля не требуется. Вместо этого следует реализо­
вать метод getLastModif i e d и предоставить стандартному методу s e r v i c e об­
рабатывать условные запросы.

Location
Данное поле заголовка включается в состав всех ответов, для которых код состоя­
ния начинается с цифры 3, и сообщает броузеру адрес документа. Броузер автома­
тически устанавливает соединение с указанным узлом и получает документ. Часто
значение данного поля устанавливается с помощью метода s e n d R e d i r e c t класса
H t t p S e r v l e t R e s p o n s e , который также устанавливает код состояния, равный 302.
Пример использования этого метода был приведен в предыдущем разделе.

Pragma
Если в состав заголовка ответа включено поле Pragma, содержащее значение п о -
cache, клиент, поддерживающий протокол HTTP 1.0, не должен сохранять полу­
ченный документ в кэше. В HTTP 1.1 аналогичные действия выполняет поле
Cache-Control: no-cache.

Refresh
Данный заголовок указывает время (в секундах), через которое броузер должен
обратиться за обновленным вариантом Web-страницы. Например, чтобы броузер
запрашивал новую копию документа каждые 30 секунд, вы должны установить зна­
чение 30 следующим образом:
response.setlntHeader("Refresh", 30)
Заметьте, что поле R e f r e s h не задает постоянное обновление документа. Оно
лишь указывает, через какое время должно произойти следующее обращение кли­
ента. Продолжая передавать поле Refresh, вы можете сформировать ответ с ко­
дом 204 (No Content), чтобы предотвратить копирование документа. Пример ис­
пользования поля R e f r e s h будет приведен ниже в этом разделе.
Вместо повторной загрузки текущего документа вы можете указать новую Web-
страницу. Для этого после значения, определяющего время обновления, задайте
точку с запятой и URL страницы. Например, чтобы броузер загружал через пять
секунд документ, расположенный по адресу h t t p : / / h o s t / p a t h , надо вызвать ме­
тод s e t Header следующим образом:
response.setHeader("Refresh", "5; URL=http://host/path/")
19.10. Ответ сервлета: поля заголовка 871

Такую установку можно использовать для кратковременного отображения ввод­


ной информации, за которой следует основной документ.
Рассматриваемый заголовок устанавливается с помощью следующего дескриптора,
помещенного в раздел HEAD HTML-документа.
<МЕТА HTTP-EQUIV="Refresh"
C0NTENT="5; URL=http://host/path/">

Этим способом часто пользуются авторы статических Web-страниц. При создании


сервлета проще непосредственно установить значение данного поля.
Поле Refresh не предусмотрено в спецификации HTTP 1.1, однако данное рас­
ширение протокола поддерживает как Netscape, так и Internet Explorer.

Retry-After
Данное поле заголовка используется с кодом состояния 503 ( S e r v i c e
U n a v a i l a b l e ) и сообщает броузеру, как скоро он должен повторить запрос.

Set-Cookie
Поле S e t - C o o k i e содержит запись cookie, связанную с Web-страницей. Для каж­
дой записи cookie требуется отдельное поле S e t - C o o k i e . В сервлетах нельзя ис­
пользовать вызов r e s p o n s e . s e t H e a d e r ( " S e t - C o o k i e " , . . . ), вместо этого на­
до применять метод addCookie класса H t t p S e r v l e t R e s p o n s e . Подробно записи
cookie рассматриваются в разделе 19.11. Поле S e t - C o o k i e не является частью
протокола HTTP 1.1. Средства cookie были предложены Netscape, а в настоящее
время их поддерживают и Netscape, и Internet Explorer.

www-Authenticate
Данное поле всегда включается в состав заголовка ответа с кодом состояния 401
(Unauthorized). Оно сообщает броузеру, какая информация должна содержаться
в поле A u t h o r i z a t i o n . Чаще всего сервлеты не выполняют непосредственных
действий с этим полем, а позволяют специализированным средствам Web-сервера
обрабатывать документы, защищенные паролем.

Постоянное состояние сервлета и автоматическа


перезагрузка \Л/еЬ'Страниц
Сервлет, представленный в следующем примере, генерирует набор простых чи­
сел. Если число содержит большое количество цифр (например, 150), процесс его на­
хождения может занять достаточно длительное время, поэтому сервлет немедленно
возвращает боузеру начальные результаты, а затем продолжает вычисления в потоке
с низким приоритетом. Благодаря такому подходу выполнение одной конкретной за­
дачи существенно не сказывается на общей производительности Web-сервера. Если
вычисления не завершены, сервлет включает в состав ответа заголовок Refresh, по­
лучив который броузер должен повторить запрос через несколько секунд.
872 Глава 19. Java на стороне сервера: сервлеты

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


казывает и другие возможности сервлета. Во-первых, он демонстрирует, что сервлет
может одновременно поддерживать несколько соединений, каждому из которых со­
ответствует отдельный поток. Во-вторых, из данного примера видно, насколько про­
сто сохраняется состояние сервлета; в традиционных CGI-программах для этого при­
ходится принимать специальные меры. На сервере выполняется только один экземп­
ляр сервлета и при получении очередного запроса создается поток, в котором
запускается метод s e r v i c e (из него вызываются методы d o G e t и d o P o s t ) . П р и этом,
чтобы данные могли быть использованы разными потоками, они должны быть поме­
щены в обычные переменные экземпляра. П р и загрузке броузером новой Web-
страницы сервер может продолжать вычисления и сохранять в памяти N последних
результатов. Если какие-либо из этих результатов соответствуют условиям очередного
запроса, сервер немедленно передает их броузеру. Очевидно, что к сервлету также
предъявляются требования по синхронизации доступа к разделяемым данным. Ин­
формация содержится в объекте S e r v l e t C o n t e x t , получить который можно с по­
мощью метода g e t S e r v l e t C o n t e x t . В классе S e r v l e t C o n t e x t определены методы
s e t A t t r i b u t e и g e t A t t r i b u t e , которые позволяют хранить произвольные данные,
связав с ними некоторые ключевые значения. Различие между хранением данных в
переменных экземпляра и объекте S e r v l e t C o n t e x t состоит в том, что S e r v l e t ­
C o n t e x t используется всеми экземплярами сервлета, выполняющимися в среде, соз­
данной сервером (или, если такая возможность поддерживается, Web-приложением).
Основной класс сервлета показан в листинге 19.17. Сервлет принимает запрос, в
котором содержатся два параметра: n u m P r i m e s и n u m D i g i t s . Значения этих пара­
метров вводит пользователь, и они передаются на сервер средствами HTML-формы.
В листинге 19,18 показан HTML-код страницы, содержащей форму, а на рис. 19.11 —
результаты ее отображения в окне броузера. Эти параметры преобразуются в цело­
численные значения с помощью вспомогательного метода g e t I n t P a r a m e t e r , в теле
которого вызывается метод I n t e g e r . p a r s e i n t (см. листинг 19.21). Значения пара­
метров с помощью метода f i n d P r i m e L i s t сравниваются с результатами предыдущих
вычислений и незавершенных вычислений, и сервлет определяет, совпадают ли ус­
ловия текущего запроса с условиями одного из запросов, принятых ранее. Затем
P r i m e L i s t проверяется, чтобы определить, найдены ли требуемые простые числа.
Если вычисления не завершены, сервер передает клиенту в составе ответа поле заго­
ловка R e f r e s h . Получив такой ответ, клиент через несколько секунд повторяет за­
прос. Независимо от того, были ли закончены вычисления, доступные на данный мо­
мент результаты передаются к л и е т у . Web-страница, содержащая частичные резуль­
таты, показана на рис. 19.12, а результаты окончательной обработки запроса изобра­
жены на рис. 19.13.
В листингах 19.19 ( P r i m e L i s t . J a v a ) и 19.20 ( P r i m e s . J a v a ) представлены до­
полнительные коды, используемые сервлетом. Класс P r i m e L i s t поддерживает по­
ток, создающий список простых чисел. Класс P r i m e реализует низкоуровневый алго­
ритм выбора числа, содержащего указанное количество ц и ф р , и нахождение просто­
го числа, равного или превышающего это значение. Для определения того, является
ли число простым, используются встроенные методы класса B i g l n t e g e r .
19.10. Ответ сервлета: поля заголовка 873

Листинг 1 9 . 1 7 . P r i m e N u m b e r s . J a v a

package cwp;

import java.io.*;
import javax.servlet.*;
import javax. servlet.http. "^;
import java.util.*;

/*"^ Сервлет, обрабатывающий запрос, генерирует n простых


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

public class PrimeNumbers extends HttpServlet {


private Vector primeListVector = new Vector();
private int maxPrimeLists = 30;

public void doGet(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, lOException {
int numPrimes =
ServletUtilities.getlntParameter(request,
"numPrimes", 50);
int numDigits =
ServletUtilities.getlntParameter(request,
"numDigits", 120);
PrimeList primeList =
findPrimeList(primeListVector, numPrimes, numDigits);
if (primeList == null) {
primeList = new PrimeList(numPrimes, numDigits, true);
// Различные потоки сервлета совместно используют
// переменные экземпляра PrimeNumbers. Поэтому
// обращение к полям сервлета синхронизируется,
synchronized(primeListVector) {
if (primeListVector.size() >= maxPrimeLists)
primeListVector.removeElementAt(0);
primeListVector.addElement(primeList);
}
}
Vector currentPrimes = primeList.getPrimes();
int numCurrentPrimes = currentPrimes.size();
int numPrimesRemaining = (numPrimes - numCurrentPrimes);
boolean isLastResult = (numPrimesRemaining == 0 ) ;
if (!isLastResult) {
response.setHeader("Refresh", " 5 " ) ;
}
874 Глава 19. Java на стороне сервера: сервлеты

response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Some " + numDigits + "-Digit Prime Numbers";
out.printIn(ServletUtilities.headWithTitie(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2 ALIGN=CENTER>" + title + "</H2>\n" +
"<H3>Primes found with " + numDigits +
" or more digits: " + numCurrentPrimes +
".</H3>");
if (isLastResult)
out.println("<B>Done searching.</B>");
else
out.println("<B>Still looking for " + numPrimesRemaining +
" more<BLINK>...</BLINK></B>");
out.println("<0L>") ;
for(int i=0; i<numCurrentPrimes; i++) {
out.println(" <LI>" + currentPrimes.elementAt(i));
}
out.println("</0L>");
out.println("</BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
doGet(request, response);
}
// Выяснить, имеются ли результаты или ведутся ли вычисления
// такого же количества простых чисел с таким же количеством
// цифр. Если подходящие результаты существуют, они
// возвращаются броузеру, а новый поток не создается.
// Размеры списка результатов невелики, и его поддержка
/ / н е требует большого объема памяти. Доступ к списку
// синхронизируется.

private PrimeList findPrimeList(Vector primeListVector,


int numPrimes,
int numDigits) {
synchronized(primeListVector) {
for(int i=0; i<primeListVector.size(); i++) {
PrimeList primes =
(PrimeList)primeListVector.elementAt(i);
if ((numPrimes == primes.numPrimes()) &&
(numDigits == primes.numDigits()))
return(primes);
}
return(null);
}
}
19.10. Ответ сервлета: поля заголовка 875

Листинг 19.18. PrimeNiimbers. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Finding Large Prime Numbers</TITLE>
</HEAD>

<BODY BGC0L0R="#FDF5E6">
<H2 ALIGN="CENTER">Finding Large Prime Numbers</H2>
<BR><BR>
<CENTER>
<FORM A C T I O N = " / s e r v l e t / c w p . PrimeN\ambers">
<B>Number of primes to calculate:</B>
<INPUT TYPE="TEXT" NAME="numPrimes" VALUE=25 SIZE=4><BR>
<B>Number of digits:</B>
<INPUT TYPE="TEXT" NAME="numDigits" VALUE=150 SIZE=3><BR>
<INPUT TYPE="SUBMIT" VALUE="Start Calculating">
</FORM>'
</CENTER>
</BODY>
</HTML>

ШШ
-^^ ^ 3 .Й ^^- Si s ui 3 Jl
Finding Large Prime Numbers

Number of primes to calculate: j i s


Niunber of digits ji^S
Start Calct^eting J

f-f"^ Document. Dor» '^ ^ Ш. ^^.J


Рис. 19.11, Внешний вид документа PrimeNumbers.html, ис­
пользуемого в качестве интерфейса к сервлету PrimeNumbers
876 Глава 19. Java на стороне сервера: сервлеты

i-inixf
^тБшшшяшшшшшшшт
^- -У 3 '^ ^ ^ ^s ^ 3 Ш
Some 125-Digit Prime Numbers
Primes fomid with 125 or mote digits: S.

Still lookiiig for 7 more

1. 25955811439632246588281060898749698746124929689978474726713518542454
2. 25955811439632246588281060898749698746124929689978474726713518542454
3. 25955811439632246588281060898749698746124929689978474726713518542454
4. 25955811439632246588281060898749698746124929689978474726713518542454
5. 25955811439632246588281060898749698746124929689978474726713518542454
6. 25955811439632246588281060898749698746124929689978474726713518542454
7. 25955811439632246588281060898749698746124929689978474726713518542454
8 25955811439632246588281060898749698746124929689978474726713518542454

±1
:У«;==й>-: ,GI
Рис. 19.12. Промежуточные результаты обработки запроса
сервлетом PrimeNumbers. Эти данные могут отображаться
после очередной автоматической перезагрузки страницы либо в
том случае, если сервлет воспользовался результатами обра­
ботки одного из предыдущих запросов. В любом случае для
получения обновленных данных броузер должен повторно обра­
титься к сервлету

58^ Some 125-Digit Piirae Nuinbet» - Netscape


Lie %Ф yiew Й0 Cownumca^a help
^^ J 3 <^ ^ ^ ^:% ^'
Э 1? Ш
Some 125-Digit Prime Numbers
Primes fomid wirli 125 or more flights: 15.

Done searching;

1. 2595581 14396322465882810608987496987461249296899784747267135185424
2. 2595581 14396322465882810608987496987461249296899784747267135185424
3. 2595581 14396322465882810608987496987461249296899784747267135185424
4. 2595581 14396322465882810608987496987461249296899784747267135185424
5. 2595581 14396322465882810608987496987461249296899784747267135185424
6. 2595581 14396322465882810608987496987461249296899784747267135185424
7. 2595581 14396322465882810608987496987461249296899784747267135185424
8. 259558 14396322465882810608987496987461249296899784747267135185424
9. 2595581 14396322465882810608987496987461249296899784747267135185424
in osQ*;*;??!
Li 14'?0Л^О0Л^'^ЙЙГ)01ПЛПЙОЯ74аЛ<5Я7ЛЛ174000ЛЯ007О47^7')>;71?^1Я';4':>АЗ]
f^'^ii^'.
Document Done ':^.2к-._::^.Ж^.:.Ш...,.^г^ ' /

Рис. 19.13. Окончательные результаты обработки запроса сер­


влетом PrimeNumbers. Эти данные могут отображаться после
очередной автоматической перезагрузки страницы либо в том
случае, если сервлет воспользовался результатами обработки
одного из предыдущих запросов. На этом этапе броузер прекра­
щает обновление Web-страницы
19.10. Ответ сервлета: поля заголовка 877

Листинг 1 9 . 1 9 . P r i m e L i s t . J a v a

p a c k a g e cwp;

import Java . util.'^^ ;


import Java.math.Biglnteger;

/** Создание объекта Vector, содержащего простые числа.


* Обычно это происходит в низкоприоритетном фоновом потоке.
V

public class PrimeList implements Runnable {


private Vector primesFound;
private int numPrimes, numDigits;

Z*'^ Вычисление numPrimes простых чисел, каждое из которых


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

public PrimeList(int numPrimes, int numDigits,


boolean runlnBackground) {
// Для поддержки JDK 1.1 вместо ArrayList
// используется Vector.
primesFound = new Vector(numPrimes);
this.numPrimes = numPrimes;
this.numDigits = numDigits;
if (runlnBackground) {
Thread t = new Thread(this);
// Низкий приоритет потока не приводит к замедлению
// работы сервера.
t.setPriority(Thread.MIN_PRIORITY);
t. start 0 ;
} else {
run();

public void run() {


Biglnteger start = Primes.random(numDigits);
for(int i=0; i<numPrimes; i++) {
start = Primes.nextPrime(start);
synchronized(this) {
primesFound.addElement(start) ;
}
}
}
public synchronized boolean isDoneO {
return(primesFound.size() == numPrimes);
878 Глава 19. Java на стороне сервера: сервлеты

public synchronized Vector getPrimesO {


if (isDoneO)
return(primesFound);
else
return((Vector)primesFound.clone()) ;
}
public int numDigitsO {
return(numDigits);
}
public int numPrimesO {
return(numPrimes);
}
public synchronized int numCalculatedPrimes() {
return(primesFound.size ());
}
}

Листинг 19.20.Primes.Java

package cwp;

import Java.math.Biglnteger;

/** Вспомогательные методы, предназначенные для генерации


* объектов Biglnteger и поиска следующего простого
* числа после Biglnteger.

public class Primes {


// Значение Biglnteger.ZERO появилось только в JDK 1.2.
private static final Biglnteger ZERO = new Biglnteger("0"),
private static final Biglnteger ONE = new Biglnteger("1");
private static final Biglnteger TWO = new Biglnteger("2");

private static final int ERR_VAL = 100;

public static Biglnteger nextPrime(Biglnteger start) {


if (isEven(start))
start = start.add(ONE);
else
start = start.add(TWO);
if (start.isProbablePrime(ERR_VAL))
return(start);
else
return(nextPrime(start));
}
private static boolean isEven(Biglnteger n) {
return(n.mod(TWO).equals(ZERO));
}
private static StringBuffer[] digits =
19.10. Ответ сервлета: поля заголовка 879

{ new StringBuffer("О")/ new StringBuffer("1")/


new StringBuffer("2") , new StringBuffer("3"),
new StringBuffer("4"), new StringBuffer("5").
new StringBuffer("6") / new StringBuffer("7")^
new StringBuffer("8")/ new StringBuffer("9") }/
private static StringBuffer randomDigit() {
int index = (int)Math.floor(Math.random() * 10);
return(digits[index] ) ;
}

public static Biglnteger random(int numDigits) {


StringBuffer s = new StringBuffer("");
for(int i=0; i<numDigits; i++) {
s.append(randomDigit() ) ;
}
return(new Biglnteger(s.toString()));
}
/** Простая программа тестирования, запускаемая из
* командной строки. В командной строке задается
* число цифр, после чего программа выбирает простое
* число указанной длины и выводит 50 следующих за ним
* простых чисел.
V
public static void main(String[] args) {
int numDigits;
if (args.length > 0)
numDigits = Integer.parseint(args[0]);
else
numDigits = 150;
Biglnteger start = random(numDigits);
for(int i=0; i<50; i++) {
start = nextPrime(start);
System.out.println("Prime " + i + " = " + start);
}
}

Листинг 19-21. S e r v l e t 0 t i l i t i e s . Java

package cwp;
import javax.servlet.*;
import javax.servlet.http.*;

public class ServletUtilities {

/•• Чтение параметра с указанным именем, преобразование


* в целочисленное значение, которое возвращается при
* завершении метода. Если параметр не существует
* либо есл1'г задан в некорректном формате, возвращается
* значение по умолчанию.
880 Глава 19. Java на стороне сервера: сервлеты

V
public static int getlntParameter(HttpServletRequest request.
String paramName,
int defaultValue) {
String paramString = request.getParameter(paramName);
int paramValue;
try {
paramValue = Integer.parseint(paramString);
} catch(NumberFormatException nfe) { // null или
// некорректный формат
paramValue = defaultValue;
}
return(paramValue);
}

1 9 . 1 1 . Cookie
Запись cookie представляет собой небольшой фрагмент текста, который Web-
сервер передает броузеру и который броузер должен вернуть при последующем визи­
те на тот же сервер или в тот же домен. Возможность сервера получать информацию,
переданную ранее клиенту, позволяет представить док}^меит в том виде, в котором
пользователь в прошлый визит завершил с ним работу, автоматически идентифици­
ровать пользователя и выполнять другие важные задачи. Большинство броузеров не
сохраняет в кэше документы, с которыми связаны записи cookie.
В данном разделе рассматриваются вопросы установки и чтения записей cookie в серв-
летах, а следующий раздел посвящен поддержке сеанса взаимодействия с сервлетом.

Преимущества использования cookie


Анализируя опыт применения cookie, можно выделить четыре основных типа за­
дач, решаемых с использованием данного средства.

Идентификация пользователя в ходе сеанса


Многие интерактивные магазины используют струк1уру, выполняющую функции
"корзины с товарами". Посетитель магазина выбирает требуемый продукт, добавляет
его в корзину и продолжает выбор товаров. Поскольку после передачи каждой Web-
страницы HTTP-соединение разрывается, то при создании программного обеспече­
ния, поддерживающего работу магазина, приходится решать вопрос идентификации!
пользователя. Программа должна определить, является ли пользователь, установив­
ший соединение, тем самым пользователем, который добавил товар в корзину. По­
стоянные HTTP-соединения не решают .эт)^ проблему, поскольк}' через такие соеди­
нения могут передаваться лишь запросы, непосредственно следующие один за дру­
гим, как, например, обращения за изображениями, содержащимися в составе Web-
с т р а ш щ ы . Кроме того, некоторые броузеры не поддерживают постоянные соедине­
ния. Данная проблема может быть решена с помощью записей cookie. Средства cookie
1 9 . 1 1 . Cookie 881

используются настолько часто, что в API сервлетов специально реализованы функции


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

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


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

Настройка узла
Многие узлы, реализованные как "порталы", предоставляют возможность изменять
внешний вид главной страницы в соответствии со вк)тами пользователя. Они позволя­
ют разместить на страгп^це прогноз погоды, данные о котировке акций, результаты
спортивных соревнований и прочие данные. Поскольку' настраивать главную страницу
при каждом визите на узел неудобно, для запоминания установок, выполненных ранее,
применяются средства cookie. Если настройка Web-страницы не предполагает сложных
действий, требуемый набор параметров может непосредственно сохраняться в записи
cookie. Если же для запоминания состояния док)^1ента необходимо хранить большой
объем данных, то клиенту передается лишь идентификатор, по которому сервер нахо­
дит в базе данных необходимые значения характеристик.

Подборка рекламных материалов


Многие Web-узлы, размещающие на своих страницах рекламные материалы, вы­
полняют целевую подборку рекламных материалов. Рекламодатель согласен выделять
дополнительные средства на то, чтобы пользователь получал объявления, близкие к
сфере его интересов. Например, если, работая с поисковым сервером, вы введете
ключевые слова "Java Servlets", то программные средства, размещенные на сервере,
могут определить, что для вас больше подходят сообщения о системах, ориентиро­
ванных на разработку сервлетов, а не объявления туристических агентств, специали­
зирующихся на поездках в Индонезию. Если же вы начнете поиск по к.чючевым сло­
вам "Java Hotels", ситуация будет обратной. П р и отсутствии cookie сервер не смог бы
сделать вывод об интересах пользователя, в очередной раз обратившегося к узлу. Для
этого ему пришлось бы дожидаться, пока пользователь введет ключевые слова.
882 Глава 19. Java на стороне сервера: сервлеты

Проблемы у возникающие при использовании


cookie
Основная задача cookie — предоставлять дополнительные возможности, облег-
чающие работу пользователей и авторов Web-страниц. Следует признать, что cookie
не могут представлять никакой серьезной угрозы для безопасности системы. Записи
cookie ни при каких условиях не запускаются на выполнение, поэтому не могут быть
использованы для доставки вирусов. Поскольку броузер хранит не более 300 cookie,
причем только 20 для каждого узла, а размеры записи cookie не превышают 4 Кбайт,
то cookie не могут быть использованы для организации атаки с целью вывода системы
из строя, например для переполнения диска.
Несмотря на то что cookie не оказывают существенного влияния на систему в це­
лом, они могут представлять угрозу секретности данных. Так, например, некоторым
пользователям может не понравиться тот факт, что поисковый сервер имеет возмож­
ность отследить их действия и выяснить, что некоторыми вопросами они интересу­
ются больше, чем другими. Если сотрудник компании обращался к серверу за списком
вакансий, вряд ли он захочет, чтобы при следующем сеансе работы в Internet в окне
броузера отобразилось соответствующее рекламное сообщение, особенно если экран
компьютера находится в поле зрения его непосредственного начальника. Возможна
ситуация, при которой узлы А и Б обращаются на узел В за некоторым изображением,
причем узел В использует cookie. В результате одни и те же данные будут переданы
как на узел А, так и на узел Б. (Netscape позволяет отключать запись cookie, передан­
ных с узлов, отличных от узла, с которым в данный момент поддерживается соедине­
ние.) Возможность связывания cookie с изображениями может также быть использо­
вана для отслеживания почты. Так, вам может прийти письмо, содержащее изобра­
жение, с которым связана запись cookie. П р и последующем обращении на некоторый
Web-узел на него будут также переданы ваш почтовый адрес и другие сведения о вас.
Еще одна проблема возникает, если cookie применяются для сохранения секрет­
ных данных. Н а п р и м е р , некоторые интерактивные книжные магазины используют
cookie для хранения информации о пользователях так, что при повторном визите
пользователю не приходится регистрироваться. Это не создает больших проблем, по­
скольку номер платежной карточки не записывается, и максимум, что можно сделать
на основании имеющейся информации,— о ф о р м и т ь .заказ на книги. Если запись
cookie попадет в руки постороннего, он сможет лишь сформировать ложный заказ,
который несложно аннулировать. Однако другие компании не столь осторожны, а
потому злоумышленник, в руки которого попадает запись cookie, получит дост)^п к
важной информации о пользователе. Хуже того, неопытные разработчики, вместо
того, чтобы хранить номер платежной карточки на сервере и посылать в составе
cookie условный код, непосредственно включают имя пользователя и номер карточки
в запись cookie. После этого оставить компьютер без присмотра становится не менее
опасно, чем забыть платежную карточку на столе.
Какие же выводы можно сделать из сказанного выше? Следует ли запрещать под­
держку cookie броузером? Некоторые пользователи поступают так, стремясь предот­
вратить возникновение проблем. Как автор сервлетов, использующих cookie, вы долж­
ны соблюдать максимальную осторожность, не передавать посредством cookie секрет­
ные данные, поскольку записи cookie легко могут стать доступны посторонним лицам.
19.11. Cookie 883

Средства API, предназначенные для работы


с cookie
Для того чтобы передать cookie клиенту, надо создать одну или несколько записей
cookie, определив соответствующие имена и значения (new C o o k i e (name, v a l u e ) ) ,
установить требуемые атрибуты с помощью методов c o o k i e . setXxx (впоследствии эти
значения можно прочитать посредством метода c o o k i e . getX)cx) и включить c o o k i e в
состав заголовка, вызвав для этого метод r e s p o n s e . a d d C o o k i e ( c o o k i e ) . Для того
чтобы прочитать запись cookie, переданную клиентом, сервлет должен вызвать метод
r e q u e s t . g e t C o o k i e s , который вернет массив объектов C o o k i e . Каждый из элементов
полученного массива соответствует записи cookie, хранящейся в броузере и связанной с
вашим сервером (если записи cookie отсутствуют в составе запроса, возвращается мас­
сив нулевой длины, но не объект n u l l ) . В большинстве случаев сервер просматривает
содержимое массива, находит cookie с интересующим именем (getName), а затем опре­
деляет значение, связанное с этим именем, вызывая метод g e t V a l u e . ЬСаждое из этих
действий подробно рассматривается ниже.

Создание cookie
Для создания cookie надо вызвать конструктор Cookie, передав ему два строковых
параметра: имя и значение cookie. Н и имя, ни значение не должны содержать сле­
дующие символы:
[ ] ( ) = , « / ? @ : ;

Включение cookie в состав заголовка ответа


Запись cookie передается клиенту в поле заголовка S e t - C o o k i e , которое включается
в состав ответа посредством метода a d d C o o k i e класса H t t p S e r v l e t R e s p o n s e . За­
метьте, что метод называется не s e t C o o k i e , а a d d C o o k i e , поскольку при включении
нового поля S e t - C o o k i e остальные поля с тем же именем остаются неизменными.
Cookie u s e r C o o k i e = new C o o k i e ( " u s e r " , "uidl234");
userCookie.setMaxAge(60*60*24*365); / / 1 год
response.addCookie(userCookie);

Чтение записей cookie, переданных клиентом


Для того чтобы прочитать значение cookie, передаваемое клиентом серверу, надо
вызвать метод g e t C o o k i e s класса H t t p S e r v l e t R e q u e s t . Данный метод вернет мас­
сив объектов C o o k i e ; каждый элемент массива соответствует полю C o o k i e в составе
заголовка HTTP-запроса. После получения массива объектов C o o k i e его элементы
проверяются на совпадение с требуемым именем. Для проверки надо вызвать метод
getName каждого объекта C o o k i e . После того как имя будет найдено, следует вызвать
метод g e t V a l u e и выполнить необходимую обработку. В конце данного раздела мы
представим два вспомогательных метода, позволяющих извлекать значение cookie с
указанным именем.
884 Глава 19. Java на стороне сервера: сервлеты

Атрибуты cookie
Перед тем как включать запись cookie в заголовок ответа, необходимо задать не­
которые характеристики cookie, используя для этого методы s e t X x x (где Ххх опреде­
ляет имя атрибута). Каждому методу s e t X x x соответствует метод getXxx, позволяю­
щий прочитать значение атрибута. В отличие от имени и значения, атрибуты cookie
применимы только к записям cookie, которые сервер передает клиенту; в составе
cookie, возвращаемых клиентом серверу, атрибуты отсутствуют.

public String g e t C o m m e n t O
public void s e t C o m m e n t ( S t r i n g c o m m e n t )
Метод s e t C o m m e n t устанавливает, a метод getComment возвращает строку ком­
ментариев, связанную с cookie. Согласно версии О протокола Cookie (см. описание
методов g e t V e r s i o n и s e t V e r s i o n ) , комментарии используются исключительно
на стороне сервера и клиенту не передаются.

public String getDomain()


public void setDomain(String domainPattern)
Метод s e t D o m a i n устанавливает, a метод g e t D o m a i n возвращает адрес домена,
соответствующего записи cookie. Вы можете использовать метод s e t D o m a i n для
того, чтобы указать броузеру, что запись cookie должна передаваться всем серве­
рам, принадлежащим заданному домену. Для того чтобы исключить передачу запи­
сей cookie слишком большому числу узлов, имя домена, передаваемое в качестве
параметра методу' s e t D o m a i n , должно содержать не меньше двух точек для доме­
нов, не включающих код страны (. com, . e d u , . gov и т.д), и не меньше трех точек
для доменов, которые заканчиваются кодом страны (. со . ик, . e d u . e s и т.д). На­
пример, записи cookie, переданные сервером b a l i . v a c a t i o n s . c o m , в обычных
условиях не будут приходить в составе запросов серверу m e x i c o . v a c a t i o n s . com.
Чтобы это происходило, сервлет должен специально вызывать c o o k i e . s e t -
Domain ( " . v a c a t i o n s . c o m " ) .

public int getMaxAge()


public void setMaxAge(int lifetime)
Метод setMaxAge устанавливает, a метод getMaxAge возвращает время жизни
cookie, т.е. интервал времени (в секундах), по истечении которого запись cookie
будет считаться устаревшей. Отрицательные значения, устанавливаемые по умол­
чанию, указывают на то, что запись cookie действительна только в течение теку­
щего сеанса (т.е. до тех пор, пока пользователь не перезагрузит броузер) и не
должна сохраняться на диске. В листинге 19.25 представлен подкласс класса
C o o k i e с именем L o n g L i v e d C o o k i e , в котором автоматически устанавливается
время жизни, равное одному году. Значение О указывает броузеру на то, что запись
cookie должна быть удалена.
1 9 . 1 1 . Cookie 885

public String getName()


public void setName(String cookieName)
Данные методы позволяют определить или установить значение cookie. Имя и
значение представляют собой характеристики cookie, которые практически все­
гда должны быть определены. Однако, поскольку имя задается при вызове конст­
руктора Cookie, вызывать метод setName, как правило, не приходится. В отличие
от setName, метод getName используется практически для всех записей cookie,
полученных сервером. Поскольк)^ метод g e t C o o k i e s класса H t t p S e r v l e t -
Request возвращает массив объектов Cookie, чаще всего разработчики сервлетов
организуют перебор содержимого массива в цикле. При этом для получения имени
применяется метод getName, а для получения значения — метод getValue. Данная
процедура инкапсулирована в теле метода getCookieValue, представленного в
листинге 19.24.

public String getPathO


public void setPath(String path)
Метод s e t P a t h устанавливает, a метод g e t P a t h возвращает путь, связанный с
cookie. Если вы не зададите путь, броузер будет возвращать запись cookie только
при обращении к ресурсам, расположенным в каталоге, соответствующем запросу,
в ответ на который он получил данную запись, либо в его подкаталогах. Например,
если сервер передал cookie при обращении к h t t p : / / e c o m m e r c e . s i t e . c o m /
t o y s / s p e c i a l s . html, то броузер включит его в состав запроса h t t p : //ecommerce.
s i t e . c o m / t o y s / b i k e s / b e g i n n e r s . h t m l , но не использует при обращении к
h t t p : / / e c o m m . e r c e . s i t e . c o m / c d s / c l a s s i c a l . h t m l . Метод s e t P a t h позволя­
ет изменять условия передачи cookie. Например, если вы вызовете метод some-
Cookie . s e t P a t h ( " / " ) , запись cookie будет включаться при обращении к любому
ресурсу на данном сервере. Путь, указанный при вызове метода, должен содержать
текущий ресурс, т. е. вы можете расширить сферу применения cookie, но не сузить
ее. Например, сервлет, расположенный по адресу h t t p : / / w w w . m y s e r v e r . c o m /
s t o r e / c u s t - s e r v i c e / r e q u e s t , может задать путь / s t o r e / (поскольку / s t o r e /
включает / s t o r e / c u s t - s e r v i c e / ) , но не может задать / s t o r e / c u s t - s e r v i c e /
r e t u r n s / (так как этот каталог не содержит / s t o r e / c u s t - s e r v i c e / ) .

public boolean getSecure()


public void setSecure(boolean secureFlag)
Метод s e t S e c u r e позволяет установить или сбросить флаг защиты, а метод
g e t Secure дает возможность определить состояние этого флага. Если флаг уста­
новлен, это означает, что запись cookie может быть передана только через защи­
щенное (SSL) соединение. По умолчанию значение флага равно f a l s e , т. е. cookie
могут передаваться по любым канала^м.

public String getValue()


public void setValue(String cookieValue)
Метод s e t V a l u e устанавливает, a метод g e t V a l u e возвращает значение, связан­
ное с cookie. В отличие от атрибутов, имя и значение cookie задаются почти все-
886 Глава 19. Java на стороне сервера: сервлеты

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

public int getVersionO


p u b l i c void setVersion(int v e r s i o n )
Метод s e t V e r s i o n задает, a метод g e t V e r s i o n возвращает версию протокола, по
правилам которого создана запись cookie. По умолчанию предполагается версия О, ко­
торая соответствует исходной спецификации Netscape ( h t t p : //www. n e t s c a p e . com/
n e w s r e f / s t d / c o o k i e _ s p e c . h t m l ) . Версия 1, поддерживаемая относительно редко,
соответствует спецификации, содержащейся в дс^кументе RFC 2109 (ссылки на элек­
тронные версии RFC можно найти по адресу h t t p : //www. r f c - e d i t o r . o r g / ) .

Примеры установки и чтения cookie


в листинге 19.22 и на рис. 19.14 показан сервлет S e t C o o k i e s , при выполнении
которого передаются шесть записей cookie. Для трех из них время жизни установлено
по умолчанию, т.е. они должны действовать до очередной перезагрузки броузера. Три
других действительны в течение часа, независимо от того, завершит пользователь се­
анс работы с броузером или перезагрузит компьютер.
В листинге 19.23 показан сервлет, который создает таблицу, содержащую все запи­
си cookie, переданные ему в составе запроса. На рис. 19.15 показано состояние серв-
лета сразу после обращения броузера к S e t C o o k i e s . Рис. 19.16 соответствует сит)^а-
ции, при которой после обращения к S e t C o o k i e s броузер был перезапущен.

Листинг 19.22.SetCookies.Java

p a c k a g e cwp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/•• Устанавливаются шесть значений cookie. Три из них
* действительны только в течение сеанса (независимо от
* того, сколько времени он длится). Три других
* действительны в течение часа (даже если броузер
* перезагружается).
V
public class SetCookies extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
for(int i=0; i<3; i++) {
/ / П о умолчанию значение maxAge равно -1, т.е.
// запись cookie действительна только в течение
// текущего сеанса.
Cookie cookie = new Cookie("Session-Cookie-" + i,
"Cookie-Value-S" + i ) ;
response.addCookie(cookie);
1 9 . 1 1 . Cookie 887

cookie s= new Cookie("Persistent-Cookie-" + i ,


"Cookie-Value-P" + i ) ;
// Cookie действительны в течение часа, даже если
/ / з а это время броузер будет перезагружен.
cookie.setMaxAge(3600);
response.addCookie(cookie);
}
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Setting Cookies";
out.println
(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</Hl>\n" +
"There are six cookies associated with this page.\n" +
"To see them, visit the\n" +
"<A HREF=\"/servlet/cwp.ShowCookies\">\n" +
"<CODE>ShowCookies</CODE> servlet</A>.\n" +
"<P>\n" +
"Three of the cookies are associated only with the\n" +
"current session, while three are persistent.\n" +
"Quit the browser, restart, and return to the\n" +
"<CODE>ShowCookies</CODE> servlet to verify that\n" +
"the three long-lived ones persist across sessions.\n" +
"</BODY></HTML>");

1^И1И'тШ|1
£te £(й ^mi FavoKes loefe Нф

~3
Setting Cookies
There are six cookies associated with this page. To see them, visit
the showCooicieg setvlst.

Three of the cookies are associated only with the current session,
v^ue three are persistent. Quit the browser, restart, and return to the
ShowCooJcies servlet to verify that the three long-lived ones persist
across sessions.

Рис, 19.14. Результаты выполнения


l^Done ^ Loca» intranet
сервлета SetCookies

Листинг 1 9 . 2 3 . S h o w C o o k i e s . J a v a

package cwp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Создание таблицы cookie, связанных с текущей
888 Глава 19. Java на стороне сервера: сервлеты

страницей.
Ч

public class ShowCookies extends HttpServlet {


public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Active Cookies";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGG0L0R=\"#FDF5E6\">\n" +
'<H1 ALIGN=\"CENTER\">" + title + "</Hl>\n" +
'<TABLE B0RDER=1 ALIGN=\"CENTER\">\n" +
'<TR BGCOLOR=\"#FFAD00\">\n" +
<TH>Cookie Name\n" +
• <TH>Cookie Value");
Cookie[] cookies = request.getCookies () ;
if (cookies != null) {
Cookie cookie;
for(int i=0 ; Kcookies . length; i++) {
cookie = cookies[i];
out.println("<TR>\n" +
" <TD> + cookie.getName0 + "\n" +
" <TD> + cookie.getValue());
}
}
out.println("</TABLE></BODY></HTML>")

ъчтттттшттттшшшшшшшшшт fef:fgHW*ff тшрштшт5шшшшшшшшшшшт


File E* View FdvofAes loots Help
F3e £.* View Fs\'Of4e* Joote Heto

x^J ' ^
..J_I]4 -^llJ «_>^ J ' Linki ** ; ^ ' •
J J гЛ ^ _и ^ -._i' ^ _^ ' Links **

Ji
Active Cooldes Active Cookies
Cooki? Katm« Cookie Vn]u«
Session-Cookie-0 Cookie-Value-SO Persistent-Coobe-O Cookie-Value-PO
Persistent-Cookie-0 Coobe-Value-PO P ersistent- С о okic -1 С о okie - Value -P1
Session-Coobe-1 Coobe-Value-Sl Persistent-Cookie-2 Cookie-Value-P2
Persistent-Cookie-1 Coobe-Value-Pl
Session-Cookie-2 Cooloe-Value-S2
Persistent-Co окне-2 Cookie-Value-P2
J
•;^Done Igi Loccil ir*ari8t ^Oone |sjLoe^»*afiet • - '

Pvic. 19.15. Результаты обращения к сервлету Рис. 19.16. Результаты взаимодействия с


ShowCookies менее чем через час после обра­ ShowCookies менее чем через час после обра­
щения к SetCookies. Запросы к обоим сервле- щения к SetCookies. После передачи запроса
там передавались в рамках одного сеанса ра­ SetCookies броузер был перезапущен
боты с броузером
19.11. Cookie 889

Вспомогательные средства для работы с cookie


в данном разделе рассматриваются методы, позволяющие выполнять определен­
ный набор действий с записями cookie.

Поиск cookie с определенным именем


в листинге 19.24 показан фрагмент файла S e r v l e t U t i l i t i e s . J a v a , который уп­
рощает процесс получения значения cookie с заданным именем. Метод
g e t C o o k i e V a l u e перебирает в цикле массив объектов C o o k i e , возвращая значение
того объекта, имя которого удовлетворяет критерию поиска. Если ни одно имя не
совпало с заданным, возвращается значение по умолчанию. Пример использования
этого метода приведен ниже.
Cookie[] cookies = r e q u e s t . g e t C o o k i e s ( ) ;
String color =
ServletUtilities.getCookieValue(cookies, "color", "black");
S t r i n g font =
ServletUtilities.getCookieValue(cookies, "font", "Arial");
Метод g e t C o o k i e также перебирает в цикле элементы массива, но возвращает ре­
альный объект C o o k i e . Этот метод используется тогда, когда необходимо выполнять
действия с самим объектом C o o k i e , а не только с его значением.

Листинг 1 9 , 2 4 . S e r v l e t U t i l i t i e s . Java

p a c k a g e cwp;
import javax.servlet.*;
import javax.servlet.http.*;

public class ServletUtilities {


// Другие методы этого класса были рассмотрены ранее.

/** Данный метод старается найти в массиве Cookie


^ запись с указанным именем. Если такой объект не найден,
* возвращается значение по умолчанию.

public static String getCookieValue(Cookie[] cookies,


String cookieName,
String defaultValue) {
if (cookies ?= null) {
for(int i=0; Kcookies. length; i++) {
Cookie cookie = cookies[i];
if (cookieName.equals(cookie.getName()))
return(cookie.getValue());
}
}
return(defaultValue);
}
/** Данный метод старается найти в массиве Cookie
890 Глава 19. Java на стороне сервера: сервлеты

* запись с указанным именем. Если такой объект не найден,


* возвращается значение null.

public static Cookie getCookie(Cookie[] cookies,


String cookieName) {
if (cookies != null) {
for(int i=0; i<cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookieName.equals(cookie.getName()))
return(cookie);
}
)
return(null);
}
}

Создание cookie с большим временем жизни


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

Листинг 19.25. LongLivedCookie. Java

package cwp;
import javax.servlet.http.*;

/** Запись cookie, действительная в течение года.


* По умолчанию cookie действует только до завершения
* текущего сеанса.
V
public class LongLivedCookie extends Cookie {
public static final int SECONDS_PER_YEAR = 60*60*24*365;

public LongLivedCookie(String name. String value) {


super(name, value);
setMaxAge(SECONDS_PER_yEAR) ;
}
}

19.12. Поддержка сеанса


в данном разделе рассмотрены средства, позволяющие прослеживать перемеще­
ние посетителя по вашему узлу.
19.12. Поддержка сеанса 891

Необходимость поддержки сеанса


п р о т о к о л H T T P является протоколом без поддержки состояния сеанса. Каждый
раз, когда клиент обращается к серверу за Web-страницей, он устанавливает новое со­
единение с сервером, по окончании которого сервер не сохраняет информацию о
клиенте. Даже серверы, поддерживающие постоянное соединение, в рамках которого
гнездо используется для передачи нескольких запросов клиента, не обладают встро­
енными средствами для работы с контекстной информацией. Отсутствие контекста
приводит к возникновению некоторых проблем. Например, когда пользователь со­
вершает покупки в интерактивном магазине, приходится принимать специальные
меры для того, чтобы выяснить, какие товары он добавил в свою "корзину" и какая из
"корзин" принадлежит данному пользователю.
Для решения подобных проблем могут быть использованы записи cookie, моди­
фикация URL и скрытые поля ф о р м ы . Н и ж е кратко рассказывается, что вам придется
сделать, если вы захотите самостоятельно решить проблему поддержки сеанса (не
прибегая к встроенным средствам, используемым при создании сервлетов) каждым из
трех способов.

Использование cookie
Записи cookie можно использовать для храненияя и н ф о р м а ц и и о сеансе. Напри­
мер, сервлет может выполнять следующие действия:
String sessionID = makeUniqueString();
H a s h t a b l e s e s s i o n l n f o = new H a s h t a b l e O ;
Hashtable globalTable = f i n d T a b l e S t o r i n g S e s s i o n s ( ) ;
globalTable.put(sessionID, sessionlnfo);
Cookie s e s s i o n C o o k i e = new C o o k i e ( " J S E S S I O N I D " , s e s s i o n I D ) ;
sessionCookie.setPathC'/") ;
response.addCookie(sessionCookie);
При очередном запросе сервер может использовать таблицу g l o b a l T a b l e , чтобы
посредством идентификатора сеанса, полученного в составе записи cookie, найти
таблицу данных, соответствующих конкретному сеансу. Такой подход часто использу­
ется, однако, следует заметить, что при создании сервлета можно пользоваться высо­
коуровневыми средствами API, которые выполняют следующие задачи.
• Извлечение из набора cookie конкретной записи, содержащей идентификатор
сеанса.
• Установка времени жизни cookie.
• Связывание с каждым запросом хэш-таблицы.
• Создание уникальных идентификаторов сеанса.

Модификация URL
П р и использовании данного подхода к каждому URL, передаваемому на сервер,
присоединяются дополнительные данные, идентифицирующие текущий сеанс. Полу­
чив запрос, сервер по идентификатору находит информацию, соответствующую се­
ансу. Например, в URL h t t p : / / h o s t / p a t h / f i l e . h t m l ; j s e s s i o n i d = 1 2 3 4 присут-
892 Глава 19. Java на стороне сервера: сервлеты

ствует идентификатор сеанса (j s e s s i o n i d = 1 2 3 4 ) . Этот способ имеет определенные


преимущества по сравнению с подходом, описанным выше, так как работает даже в
том случае, когда броузер не поддерживает cookie. Однако, как и при использовании
cookie, в программе на стороне сервера должна быть предусмотрена специальная об­
работка идентификатора. Кроме того, чтобы данный подход работал, в документах,
предоставляемых пользователю, все URL, ссылающиеся на рес)^сы вашего узла (а
также URL, находящийся в поле L o c a t i o n и использующийся для перенаправления),
должны содержать дополнительные данные. Если пользователь временно прервет се­
анс, а затем возобновит его, используя закладку, данные о сеансе будут потеряны.

Скрытые поля
в составе HTML-формы могут быть элементы наподобие приведенного ниже.
<INPUT TYPE="HIDDEN" NAME="session" VALUE=",..">
При активизации ф о р м ы имя и значение этого элемента будут переданы методом
GET или POST на сервер в составе строки параметров. Скрытые поля могут приме­
няться для хранения и н ф о р м а ц и и о сеансе. Недостаток их состоит в том, что они мо­
гут использоваться только в динамически генерируемых документах.

Поддержка сеанса сервлетами


Разработчики сервлетов могу"г воспользоваться для поддержки сеанса API H t t p -
S e s s i o n . П р и создании этого высокоуровневого интерфейса были использованы
cookie и модификация URL. Для передачи информации о сеансе по умолчанию ис­
пользуется cookie, но если поддержка cookie в броузере отключена, сервлет перехо­
дит к модификации URL. Автор сервлета не обязан вникать в особенности работы
механизмов, лежащих в основе данного API. Все необходимые действия автоматиче­
ски выполняются на низком )ровне.

API поддержки сеанса


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

Получение объекта HttpSession, связанного с текущим


запросом
Для получения объекта H t t p S e s s i o n надо вызвать метод g e t S e s s i o n класса
H t t p S e r v l e t R e q u e s t . В ответ сервлет попытается извлечь идентификатор сеанса из
записи cookie, переданной клиентом, или из данных, присоединенных к URL, а затем
использует эти данные в качестве ключа для поиска созданного ранее объекта
H t t p S e s s i o n . Все эти действия "прозрачны" для программиста; ему, как было сказа­
но выше, надо лишь вызвать метод g e t S e s s i o n . Если данный метод возвращает
n u l l , это означает, что взаимодействие только что началось и объект сеанса еще не
19.12. Поддержка сеанса 893

создан. Для того чтобы объект сеанса создавался автоматически, надо при вызове ме­
тода g e t S e s s i o n передать ему параметр t r u e . Вызов метода выглядит следующим
образом:
HttpSession s e s s i o n = r e q u e s t . g e t S e s s i o n ( t r u e ) ;
Если вас интересует, существовал ли объект сеанса ранее или был создан при по­
следнем вызове метода g e t S e s s i o n , вы можете использовать для проверки метод
isNew.

Получение информации, соответствующей сеансу


Объект H t t p S e s s i o n располагается на стороне сервера и автоматически связыва­
ется с клиентом посредством механизма cookie или модификации URL. Структура
объекта сеанса позволяет хранить в нем любое количество ключей и соответствую­
щих им данных. В версии 2.1 и более ранних версиях API сервлета для поиска значе­
ний, сохраненных ранее, используется вызов s e s s i o n . g e t V a l u e ( " a t t r i b u t e " ).
Метод g e t V a l u e возвращает значение Object, которое необходимо привести к тре­
буемому типу. Значение n u l l указывает на то, что атрибут с таким именем отсутству­
ет. Таким образом, помещая новый атрибут в состав объекта HttpSession^ полезно
вызвать метод g e t V a l u e и проверить возвращаемое значение.
В версии 2.2 API сервлета метод g e t V a l u e не рекомендован для применения. Вме­
сто него используется метод g e t At t r i b u t e , поскольку он лучше сочетается с именем
s e t A t t r i b u t e (в версии 2.1 была определена пара методов g e t V a l u e и putValue).
Пример использования объекта H t t p S e s s i o n приведен ниже. Здесь предполага­
ется, что класс ShoppingCart был создан ранее и в нем были реализованы возмож­
ности хранения информации о приобретаемых товарах.
ShoppingCart c a r t =
(ShoppingCart)session.getAttribute("shoppingCart") ;
if ( c a r t == n u l l ) { / / "Корзина" не создана
c a r t = new ShoppingCart();
session.setAttribute("shoppingCart", cart);
}
doSomethingWith(cart) ;
В большинстве случаев при получении значения атрибута указывается конкретное
имя. Однако вы можете также получить имена всех атрибутов, вызвав метод
getValueNames, возвращающий строковый массив. В версии 2.1 это единственная
возможность определить имена атрибутов. В версии 2.2 был также предусмотрен мс
тод g e t A t t r i b u t e N a m e s , возвращающий значение Enumeration. Имя этого метода
легко запоминается по аналогии с методами getHeaderNames и getParameterFames
класса H t t p S e r v l e t R e q u e s t .
В большинстве случаев вас будут, конечно же, интересовать данные, связанные с
сеансом, однако класс H t t p S e s s i o n позволяет получить и другую информацию. Не­
которые методы этого класса описаны ниже.
894 Глава 19. Java на стороне сервера: сервлеты

public Object getValue(String name)


public Object getAttribute(String name)
Эти методы позволяют извлечь из объекта сеанса хранящиеся в нем значения. Ес­
ли атрибут с указанным именем отсутствует, любой из указанных методов возвра­
щает значение n u l l . Работая с версией 2.1 API сервлетов, следует использовать
метод g e t V a l u e . В версии 2.2 метод g e t V a l u e не рекомендован к применению;
вместо него используется g e t A t t r i b u t e .

public void putValue(String name, Object value)


public void setAttribute(String name, Object value)
Каждый из этих методов связывает значение с именем. В версии 2.1 используется
метод putValue, а в версии 2.2— метод s e t A t t r i b u t e (putValue не рекомендо­
ван для применения). Если объект, переданный putValue или s e t A t t r i b u t e ,
реализует интерфейс H t t p S e s s i o n B i n d i n g L i s t e n e r , при связывании его с име­
нем вызывается метод valueBound. Аналогично, если предыдущий объект реали­
зует интерфейс H t t p S e s s i o n B i n d i n g L i s t e n e r , вызывается метод valueUnbound.

public void removeValue(String name)


public void removeAttribute(String name)
Данные методы позволяют удалить значение, связанное с указанным именем. Если
удаляемый объект реализует интерфейс H t t p S e s s i o n B i n d i n g L i s t e n e r , вызыва­
ется метод valueUnbound. При работе с версией 2.1 следует использовать метод
removeValue. В версии 2.2 желательно использовать метод r e m o v e A t t r i b u t e ;
метод removeValue продолжает поддерживаться, но не рекомендован для при­
менения.

public String[] getValueNamesO


public Enumeration getAttributeNames()
Данные методы возвращают имена атрибутов, связанных с сеансом. Работая с вер­
сией 2.1, надо использовать getValueNames. В версии 2.2 этот метод не рекомен­
дован для применения; вместо него используется метод g e t A t t r i b u t e N a m e s .

public String getld()


Метод g e t Id возвращает уникальный идентификатор сеанса. Этот метод удобно
использовать для отладки или при протоколировании.

public boolean isNew()


Данный метод возвращает t r u e , если объект сеанса был только что создан и не
использовался для поддержки взаимодействия с клиентом. В противном случае
возвращается значение f a l s e .

public long getCreationTimeO


Метод getCreationTime возвращает число миллисекунд, прошедших с полуночи 1
января 1970 г. (GMT) до момента создания объекта сеанса. Для того чтобы получить
19.12. Поддержка сеанса 895

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


объекта Date либо методу s e t T i m e l n M i l l i s объекта GregorianCalendar.

public long getLastAccessedTimeO


Данный метод возвращает число миллисекунд, прошедших с полуночи 1 января
1970 г. (GMT) до момента получения последнего запроса клиента в рамках данного
сеанса.

public int getMaxInactiveInterval()


public void setMaxInactiveInterval(int seconds)
Данные методы задают время жизни сеанса — интервал времени в секундах, в те­
чение которого взаимодействие в рамках сеанса может отсутствовать. По истече­
нии этого времени сеанс будет автоматически завершен. Отрицательное значение
указывает на то, что сеанс должен поддерживаться неограниченно долго. Заметь­
те, что время тайм-аута, установленное на сервере, не совпадает с временем жизни
сеанса, передаваемым клиенту.

public void invalidate()


Этот метод завершает сеанс и разрывает связь с объектами.

Связывание информации с сеансом


Как вы уже знаете, для получения информации, связанной с сеансом, используют­
ся методы g e t V a l u e (для версии 2.1) и g e t At t r i b u t e (для версии 2.2). Чтобы по­
местить значение в объект сеанса, надо воспользоваться методом p u t Value либо
s e t A t t r i b u t e . При вызове одного из этих методов ему передается ключевое имя и
связываемое с ним значение. Имена g e t A t t r i b u t e и s e t A t t r i b u t e лучше соответ­
ствуют g e t / s e t методам JavaBeans API. Чтобы при связывании значений выполня­
лись некоторые побочные действия, надо, чтобы объект, помещаемый в объект сеан­
са, реализовывал интерфейс H t t p S e s s i o n B i n d i n g L i s t e n e r . В этом случае при вы­
зове метода p u t Value или s e t A t t r i b u t e будет вызываться метод valueBound.
Помните, что методы p u t V a l u e и s e t A t t r i b u t e заменяют существующие значе­
ния. Если вы хотите удалить значение атрибута, не заменяя его новым, используйте
для этого метод removeValue (для версии 2.1) или метод remove At t r i b u t e (для
версии 2.2). При этом, если удаляемый объект реализует интерфейс H t t p S e s s i o n ­
B i n d i n g L i s t e n e r , при его удалении также будет вызван метод valueUnbound. Ино­
гда в процессе работы возникает необходимость заменить значение, связанное с се­
ансом (см. ключ r e f e r r i n g P a g e в приведенном ниже примере). В других случаях на­
до лишь модифицировать имеющееся значение, добавив к нему новые данные (см.
ключ p r e v i o u s Items в том же примере). В этом примере предполагается, что в клас­
се ShoppingCart реализован метод addltem, предназначенный для добавления то­
варов в "корзину", а в классе C a t a l o g имеется статический метод g e t l t e m , который
возвращает объект, представляющий изделие, по его идентификатору.
HttpSession s e s s i o n = r e q u e s t . g e t S e s s i o n ( t r u e ) ;
session.setAttribute("referringPage",
rec[uest.getHeader ("Referer")) ;
896 Глава 19. Java на стороне сервера: сервлеты

ShoppingCart cart =
(ShoppingCart)session.getAttribute("previousltems");
if (cart == null) { // "Корзина" не создана
cart = new ShoppingCart();
session.setAttribute("previousltems", cart);
}
String itemID = request.getParameter("itemID");
if (itemID != null) {
cart.addItem(Catalog.getItem(itemID));
}

Завершение сеанса
Как только время между запросами клиента превысит значение, заданное посред­
ством s e t M a x I n a c t i v e I n t e r v a l , сеанс автоматически завершается. П р и этом раз­
рывается связь объектов с ключевыми значениями. Для объектов, реализующих ин­
т е р ф е й с H t t p S e s s i o n B i n d i n g L i s t e n e r , также вызывается метод v a l u e U n b o u n d .
Вместо того чтобы дожидаться окончания времени жизни сеанса, его можно за­
вершить принудительно, вызвав метод i n v a l i d a t e .

Кодирование URL, передаваемых клиенту


Если для поддержки сеанса используется модификация URL и вы передаете кли­
енту URL, указывающий на ваш узел, вам необходимо добавить к нему данные, иден­
тифицирующие сеанс. Поскольку некоторые сервлеты и JSP автоматически начинают
применять модификацию URL, если cookie не поддерживаются клиентом, вам надо
явным образом закодировать все URL, ссылающиеся на ваш узел. Такие URL могут
встретиться в двух местах. Во-первых, это URL, встроенные в состав Web-страницы,
генерируемой клиентом. Такие URL необходимо обработать методом encodeURL
класса H t t p S e r v l e t R e s p o n s e . Этот метод определяет, используется ли модифика­
ция URL, и добавляет при необходимости информацию о сеансе. Если такой необхо­
димости нет, URL возвращаются в прежнем виде.
S t r i n g originalURL = someRelativeOrAbsoluteURL;
S t r i n g encodedURL = r e s p o n s e . e n c o d e U R L ( o r i g i n a l U R L ) ;
o u t . p r i n t l n ( " < A HREF=\"" + encodedURL 4- " \ " > . . . </A>") ;
Во-вторых, URL, ссылающиеся на ваш узел, могут использоваться при вызове
s e n d R e d i r e c t (т.е. помещаться в поле L o c a t i o n заголовка ответа). В этом случае
для определения того, требуется ли информация о сеансе, используются другие пра­
вила и метод encodeURL вызывать нельзя. Для этой цели класс H t t p S e r v l e t ­
R e s p o n s e содержит метод e n c o d e R e d i r e c t U R L , пример использования которого
приведен ниже.
S t r i n g o r i g i n a l U R L = someURL; / / В версии 2 . 2 поддерживаются
/ / относительные URL
S t r i n g encodedURL = r e s p o n s e . e n c o d e R e d i r e c t U R L ( o r i g i n a l U R L ) ;
response.sendRedirect(encodedURL);
Поскольку вы не знаете, будет ли ваш сервлет использоваться в будущем для генера­
ции последовательности документов, для которых потребуется поддержка сеанса, жела­
тельно учитывать такую возможность и кодировать URL, ссылающиеся на этот узел.
1 9 . 1 2 . Поддержка сеанса 897

М е т о д и к а профессионалов

Учитывайте будущие изменения: обрабатывайте URL, указывающие


на ваш узел, методами response. encodeURL или response, encode-
RedlrectURL, независимо от того, необходима ли в данный момент
поддержка сеанса.

Сервлет, реализующий счетчик обращений


клиента
в листинге 19.26 показан простой сервлет, который отображает основные сведения
о сеансе взаимодействия с клиентом. П р и обращении клиента сервлет вызывает метод
r e q u e s t . g e t S e s s i o n ( t r u e ) , чтобы либо получить текущий сеанс, либо создать но­
вый. Затем сервлет ищет целочисленный ( I n t e g e r ) атрибут с именем a c c e s s C o u n t .
Если этот атрибут не найден, количество предыдущих обращений считается нулевым.
Число обращений увеличивается на единицу и связывается с сеансом посредством ме­
тода p u t V a l u e . Наконец, сервлет выводит простую HTML-таблицу, отображающую
сведения о сеансе. На рис. 19.17и 19.18 показано состояние сервлета после первого об­
ращения и после того, как документ был перезагружен несколько раз.

Листинг 19.26. ShowSession. Java

p a c k a g e cwp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;
import java.util.*;
/** Простой пример поддержки сеанса. */
public class ShowSession extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Session Tracking Example";
HttpSession session = request.getSession(true);
String heading;
Integer accessCount =
(Integer)session.getAttribute("accessCount");
if (accessCount == null) {
accessCount = new Integer(0);
heading = "Welcome, Newcomer";
} else {
heading = "Welcome Back";
accessCount = new Integer(accessCount.intValue() + 1)
}
// Вместо putValue используется setAttribute.
898 Глава 19. Java на стороне сервера: сервлеты

session.setAttribute("accessCount", accessCount);
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGC0L0R=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + heading + "</Hl>\n" +
"<H2>Information on Your Session:</H2>\n" +
"<TABLE B0RDER=1 ALIGN=\"CENTER\">\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
" <TH>Info Type<TH>Value\n" +
"<TR>\n" +
" <TD>ID\n" +
" <TD>" + session. getldO + "\n" +
"<TR>\n" +
" <TD>Creation Time\n" +
ft <TD>" +
new Date(session.getCreationTime()) + "\n" +
"<TR>\n" +
" <TD>Time of Last AccessXn" +
" <TD>" +
new Date(session.getLastAccessedTime() ) + "\n" 4
"<TR>\n" +
" <TD>Nuinber of Previous Accesses\n" +
" <TD>" + accessCount + "\n" +
"</TABLE>\n" +
"</BODY></HTML>");

/** Запросы GET и POST обрабатываются одинаково. */

public void doPost(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, lOException {
doGet(request, response);
}

': Session Trackinq Example - Netscape


ie Edit ){few 00 Uwnrmmlaator Ijiefp
SSi
Welcome, Newcomer
Information on Your Session:
i Vrttae
ID Tol010mC6608409833952431At
Creation Time Mon Apr 16 10:41:25 EDT 2001
Time of Last Access Mon Apr 16 10:41:25 EDT 2001
Number of Previous Accesses 0

.jif j«#ij' " "" """ OocumenfDone


s-f m г
Рис. 19.17. Первое обращение клиента к сервлету
ShowSession
19.12. Поддержка сеанса 899

1сА-ит*д!иш1ди,1.]1Л1ДЛУШЛ1.и
Ftie Edit ytew Q,o jQomrmKBcetor йе^
^ •>" 3 Й ^^ i^a c# rf Q Ш fil
Welcome Back
Information on Your Session:

Ш Tol010mC6608409833952431At
Creation Time Mon Apr 16 10:41:25 EDT 2001
Time of Last Access Mon Apr 16 10:44:54 EDT 2001
Number of Previous Accesses 10

лаГг-Ф-, Oocument Dcxws У^Йь. 4^ ф^ СЗ \^

Рис. 19.18. Одиннадцатое по счету обращение клиента


к сервлету ShowSession. Счетчик обращений не зависит
от действий остальных клиентов

19.13. Резюме
Сервлеты представляют собой эффективное, мощное и недорогое средство, высту­
пающее в качестве альтернативы CGI-программам и другим технологиям создания про­
грамм, выполняющихся на стороне сервера. Помимо преимуществ, сервлеты имеют
важный недостаток: они должны генерировать HTML-документы. Использовать метод
p r i n t In для создания больших разделов Web-страниц неудобно, а сопровождение
сервлета, выполняющего такие действия, сопряжено со значительными трудностями.
Кроме того, код, генерирующий данные, объединен с кодом, представляющим их, что
затрудняет создание сервлета двумя отдельными группами разработчиков. Эти пробле­
мы решает технология JSP, которая будет рассмотрена в следующей главе.
JAVASERVER PAGES

В ЭТОЙ главе...

• Преимущества JSP.

• Выражения, скриптлеты и декларации.

• Управление структурой сервлета, генерируемого


на базе JSP.

• Включение файлов и аплетов в состав JSP.

• Использование JavaBeans.

• Создание библиотек дескрипторов.

• Совместное использование сервлетов и JSP. Архитектура


"модель-просмотр-контроллер".
j-:y\3j^^

2 0 . 1 . Общие сведения о JSP


Технология JSP позволяет объединить статический HTML-текст с кодом, динами­
чески генерируемым сервлетами. Для этого надо лишь создать обычный HTML-файл
(с помощью обычного текстового редактора или одного из инструментальных
средств) и включить в него специальные дескрипторы. Обычно эти дескрипторы на­
чинаются символами "<%" и заканчиваются "%>". Н и ж е приведен пример JSP, кото­
рый при получении запроса h t t p : / / h o s t / O r d e r C o n f i r m a t i o n . j s p ? t i t l e = C o r e - i -
Web-t-Programming генерирует до1сумент, содержащий сообщение " T h a n k s f o r o r ­
d e r i n g C o r e Web P r o g r a m m i n g " .
Thanks f o r o r d e r i n g <!><%= r e q u e s t . g e t P a r a m e t e r ( " t i t l e " ) %></!>
Само no себе разделение HTML-текста и динамических компонентов позволяет
добиться ряда преимуществ по сравнению с сервлетами. Кроме того, подходы, ис­
пользуемые JSP, позволяют выделить данную технологию из ряда подобных средств,
например ASP, PHP и ColdFusion. В разделе 20.2 преимущества JSP будут обсуждаться
более детально, но в основном они сводятся к двум особенностям. Во-первых, под­
держка JSP реализуется во всех новых продуктах и уже сейчас, разрабатывая JSP-
документ, вы не ограничиваете себя конкретной операционной системой или серве­
ром. Во-вторых, JSP обеспечивает полный доступ к средствам сервлетов и не требует
от вас изучения нового специализированного языка.
Подготовка JavaServer Pages, доступных из Web, происходит гораздо проще, чем
создание сервлетов. Для этого вам потребуется Web-сервер, поддерживающий JSP.
В один из каталогов, доступных этому серверу, надо поместить файл с расширением
. j s p , содержащий код JSP (большинство серверов не накладывают ограничений на
расположение документа). П р и этом не требуется ни компилировать файл, ни поме­
щать его в тот или иной пакет, ни устанавливать значение переменной окружения
CLASS PATH. Несмотря на то что вам не требуется устанавливать персональное окру-
902 Глава 2 0 . JavaServer P a g e s

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


доступ к файлам классов сервлетов и JSP и к компилятору Java. Подробные сведения о
требуемом окружении вы найдете в документации на конкретный сервер, а некото­
рые общие п р и н ц и п ы описаны в разделе 19.2.
Несмотря на то что созданный вами JSP-файл напоминает обычный HTML-
документ, тем не менее, он автоматически преобразуется в сервлет. Статический
HTML-текст передается в выходной поток, связанный с этим сервлетом. Как правило,
JSP преобразуется в сервлет при первом обращении клиента к документу. Первому
пользователю, обратившемуся к JSP, приходится достаточно долго ждать, пока на
стороне сервера JSP будет преобразован в сервлет и выполнена компиляция сервлета.
Поэтому желательно, чтобы автор JSP-документа после установки на сервер сам обра­
тился к нему.
Приступая к работе над JSP, надо учитывать следующую особенность. Если вы допус­
тите ошибку в той части JSP, которая ответственна за формирование динамического
кода, система не сможет преобразовать ваш документ в сервлет. В этом случае сервер
отправит клиенту HTML-страницу с сообщением об ошибке. Однако Internet Explorer 5
обычно заменяет такие страницы на заранее подготовленный документ с сообщением,
которое авторы броузера посчитали более "дружественным". При отладке JSP следует
отключить соответствующую опцию Internet Explorer 5.0. Для этого надо выбрать пункт
меню Tools (Сервис), перейти в меню Internet Options (Свойства обозревателя), вы­
брать вкладку Advanced (Дополнительно) и сбросить флажок опции Show friendly HTTP
error m e s s a g e s (Выводить подробные сообщения об ошибках HTTP).

М е т о д и к а профессионалов ^Ц^

Приступая к отладке JSP, убедитесь, что в Internet Explorer 5.0 от- ЩШШШк
ключей вывод подробных сообщений об ошибках. ^^^^^ш

Помимо HTML-выражений, существуют т р и типа конструкций, которые могут


входить в состав JSP-документа: элементы сценариев, директивы и действия. Элементы
сценариев позволяют задавать Java-код, который непосредственно включается в со­
став сервлета, директивы дают возможность управлять структурой сервлетов, а дей­
ствия определяют компоненты, влияющие на поведение сервера. Для того чтобы уп­
ростить задачу разработчика, в его распоряжение предоставляется ряд предопреде­
ленных переменных.
В данной главе описываются версии 1.0 и L1 спецификации JavaSei-ver Pages. По
сравнению с версией 0.92, спецификация версии 1.0 была существенно изменена, и
новые документы JSP практически несовместимы с серверами, поддерживающими
версию 0.92. Изменения, внесенные в версию 1.1, не столь существенны. Главным до­
полнением является возможность определения новых дескрипторов и использования
спецификации 2.2 для создания сервлетов. Документы JSP 1.1, не использующие поль­
зовательские дескрипторы и не включающие выражения, специфические для Servlet
2.2, могут выполняться в среде серверов, поддерживающих JSP 1.0. JSP 1.0 полностью
совместимы с серверами JSP 1.1.
2 0 . 2 . Преимущества JSP 903

20.2. Преимущества JSP


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

Преимущества JSP перед ASP и ColdFusion


Active Server Pages (ASP) — это технология, разработанная Microsoft и предназна­
ченная для решения тех же задач, что и JSP. Однако в JSP для написания динамиче­
ских фрагментов документа используется Java, а не специализированный язык, такой
как VbScript, применяемый в ASP. Поэтому JSP— более мощный инструмент по срав­
нению с ASP; это становится особенно заметно при решении сложных задач, требую­
щих применения повторно используемых компонентов. Кроме того, JSP можно легко
переносить в другие операционные системы и на другие серверы. Работая с JSP, вы не
привязаны к Windows NT/2000 и IIS, как в случае с ASP. Те же аргументы можно при­
вести, сравнивая JSP с ColdFusion.

Преимущества JSP перед РНР


РНР — свободно распространяемый инструмент, реализующий язык для написа­
ния сценариев, встраиваемый в HTML-документы. Как уже было сказано выше, для
написания динамических фрагментов в JSP используется язык Java. Кроме того, раз­
работчику доступны мощные средства API для поддержки сетевых соединений, баз
данных и распределенных объектов. Чтобы добиться того же результата при исполь­
зовании РНР, надо затратить время на изучение нового незнакомого языка. Вторым
преимуществом JSP перед РНР является наличие широкой поддержки JSP производи­
телями серверов и инструментальных средств.

Преимущества JSP перед сервлетами


JSP не предоставляет никаких возможностей, которые в принципе не могли бы
быть реализованы с помощью сервлетов. Однако преобразование JSP в сервлеты
скрыто от разработчика, а написать и поддерживать HTML-документ гораздо проще,
чем разбираться в бесчисленных вызовах p r i n t In, используемых при динамической
генерации HTML-кода сервлетом. Кроме того, разделение статического HTML-кода и
динамических фрагментов позволяет подключать к работе над JSP разных специали­
стов. Web-дизайнеры, специализирующиеся на оформлении документов, могут созда­
вать HTML-код привычными для них средствами, а Web-программисты — работать
над динамическими фрагментами. По окончании работы полученные результаты
объединяются в одном документе.
Означает ли вышесказанное, что вы должны полностью переключиться на изуче­
ние JSP и забыть о сервлетах? Это было бы ошибкой. Разработчик JSP должен знать
сервлеты по следующим причинам.
L JSP-документы преобразуются в сервлеты. Невозможно понять принципы ра­
боты JSP, не зная сервлетов.
904 Глава 20. JavaServer Pages

2. JSP состоит из статического HTML-текста, специальных дескрипторов и Java-


кода. Этот Java-код — не что иное, как код сервлета.
3. Н е к о т о р ы е задачи удобнее решать с помощью сервлетов, чем с помощью JSP.
JSP хорошо подходит для создания документов, основную часть которых со­
ставляет статический HTML-текст либо другие символьные данные. С помо­
щью сервлетов удобно генерировать двоичные данные, создавать Web-
страницы, структура которых полностью зависит от запросов клиентов, либо
выполнять перенаправление.
4. Ряд задач удобнее решать, используя сочетание сервлетов и JSP, чем с помощью
только сервлетов или только JSP. Подробно об интеграции сервлетов и JSP рас­
сказывается в разделе 20.8.

Преимущества JSP перед SSI


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

Преимущества JSP перед JavaScript


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

20.3. Элементы сценариев JSP


Элементы сценариев позволяют включать в состав сервлета, генерируемого на ос­
нове JSP, фрагменты кода. Элементы сценариев подразделяются на три категории.
L Выражения, задаваемые с помощью языковой конструкции "<%= выражение %>".
Выражения вычисляются и включаются в состав выходных данных сервлета.
2. Скриптлеты, определяемые как "<% код %>". Они помещаются в состав метода
_ j s p S e r v i c e , вызываемого из метода s e r v i c e .
3. Декларации, задаваемые в формате "<% ! код %>". Декларации включаются в
состав класса сервлета за пределами имеющихся в нем методов.
Каждый из перечисленных типов элементов сценариев подробно описывается ниже.
В большинстве случаев основную часть JSP-документа составляет статический
HTML-текст, который называют текстом шаблона. Это обычный HTML-код, подчи­
няющийся всем правилам данного языка. Текст шаблона передается сервлетом кли-
2 0 . 3 . Элементы сценариев JSP 905

енту без изменений и для его создания может быть использован любой инструмент,
предназначенный для генерации Web-страниц. Для создания документов JSP, приве­
денных в данной книге, мы использовали Allaire HomeSite.
П р и создании текста шаблона необходимо придерживаться следующих правил. Во-
первых, если вы хотите передать в выходной поток символы "<%", в текст шаблона
необходимо включить последовательность "<\%". Во-вторых, если вы хотите, чтобы
комментарии JSP не были помещены в состав сгенерированного документа, их надо
оформить в следующем виде:
<%-- Комментарии JSP --%>
Комментарии HTML, заданные в формате
<!-- Комментарии HTML -->
будут включены в состав HTML-документа.

Выражения
JSP-выражения используются для того, чтобы непосредственно включать требуе­
мые значения в состав выходных данных сервлета. Выражение задается в формате
<^o= Выражение Java %>
Выражение вычисляется, преобразуется в строковый вид и включается в состав
документа. Вычисление выражения осуществляется после получения запроса клиен­
та, при этом может использоваться информация, содержащаяся в запросе. Например,
в следующем примере выражение вю1ючает дату и время обращения клиента к JSP:
C u r r e n t t i m e : <%= new J a v a . u t i l . D a t e () %>
В результате в тело метода j s p S e r v i c e (вызываемого из метода s e r v i c e ) будут
помещены следующие строки кода:
out.print("Current time: " ) ;
out.println(new Java.util.Date ());

Предопределенные переменные
п р и вычислении выражений могут использоваться предопределенные перемен­
ные; наиболее важные из этих переменных и объекты, которым они соответствуют,
перечислены ниже. (Подробно предопределенные переменные будут рассмотрены
ниже в этом разделе.)
• request — объект H t t p S e r v l e t R e q u e s t .
• response — объект H t t p S e r v l e t R e s p o n s e .
• session— объект H t t p S e s s i o n , связанный с запросом (он может быть запре­
щен посредством атрибута s e s s i o n директивы p a g e — см. раздел 20.4).
• out — объект P r i n t W r i t e r , используемый для передачи выходных данных клиенту.
П р и м е р использования предопределенных переменных приведен ниже.
Your h o s t n a m e : <%= r e q u e s t . g e t R e m o t e H o s t ( ) %>
906 Глава 2 0 . JavaServer Pages

Синтаксис XML для составления выражений


Некоторые серверы поддерживают альтернативные синтаксические правила, ис­
пользуемые для составления JSP-выражений. В этом случае выражение представляет­
ся в следующем формате:
<jsp:expression>Выражение Java</jspiexpression>
В JSP 1.1 и более ранних версиях поддержка альтернативного синтаксиса не явля­
ется обязательным требованием. В серверах, совместимых с JSP 1.2, данный синтак­
сис должен поддерживаться в тех случаях, когда автор документа не использует ASP-
подобные выражения (<%= . . . %>).В отличие от HTML-элементов, XML-элементы
чувствительны к регистру символов; поэтому j s p : e x p r e s s i o n надо задавать симво­
лами нижнего регистра.

Установка JSP-документов
П р и создании сервлетов необходимо задавать значение переменной окружения
CLASS PATH и использовать пакеты для устранения конфликтов имен. П р и обращении
к сервлетам клиенты должны указывать специальные URL. В отличие от сервлетов,
JSP могут размещаться в тех же каталогах, что и обычные HTML-документы, изобра­
жения и листы стилей. П р и обращении к JSP указываются такие же URL, как и при
обращении к Web-страницам, изображениям и прочим подобным ресурсам. Ниже
приведено несколько примеров размещения JSP и URL, используемых для обращения
к ним (здесь h o s t означает имя или адрес узла).

Каталог, и с п о л ь з у е м ы й с е р в е р о м T o m c a t
install_dir\webapps\ROOT
install_dir\webapps\ROOT\anyDir

URL для сервера Tomcat


http://host/filename.jsp
http://host/anyDir/filename.jsp

Каталог, и с п о л ь з у е м ы й с е р в е р о м J R u n

install_diг\servers\default\default-app
install_dir\servers\default\default-app\anyDir

URL для сервера JRun

http://host/filename.jsp
http://host/anyDir/filename.jsp
2 0 . 3 . Элементы сценариев JSP 907

Несмотря на то что JSP не предполагает использование специальных каталогов,


размещение Java-классов, вызываемых из JSP, должно соответствовать стандарту для
классов сервлетов (например, . . . / W E B - I N F / c l a s s e s ) .

Пример использования выражений JSP


в листинге 10.1 приведен пример JSP-документа, а на рис. 20.1 показан результат об­
ращения к нему. При работе с Tomcat мы помещаем JSP в каталог i n s t a l l _ d i r \
webapps\ROOT\cwp\Expressions . j sp; для обращения к этому документу использует­
ся URL h t t p : / / h o s t / c w p / E x p r e s s i o n s . j sp. Заметьте, что в заголовок документа мы
включили элементы МЕТА и LINK. В обычных сервлетах эти элементы обычно не ис­
пользуются по двум причинам. Во-первых, в сервлете создание HTML-элемента означа­
ет дополнительные вызовы метода p r i n t I n . При создании JSP могут использоваться
инструментальные средства, и включить несколько дополнительных элементов не со­
ставляет большого труда. Во-вторых, в сервлетах не могут присутствовать относитель­
ные URL, поскольку преобразование URL в путь для сервлетов и для обычных Web-
страниц происходит по-разному. JSP-файлы помещаются в те же каталоги, что и HTML-
документы, поэтому относительные URL без труда могут преобразовываться в абсолют­
ные. Таким образом, файл листов стилей, указанный в элементе LINK, размещается в
том же каталоге, что и JSP-документ. Файл . e s s с исходным кодом листов стилей может
быть скопирован с сервера h t t p : / /www. c o r e w e b p r o g r a m m i n g . com/.

Листинг 2 0 , 1 . E x p r e s s i o n s . j sp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>JSP E x p r e s s i o n s < / T I T L E >
<META NAME="keywords"
CONTENT="JSP,expressions,JavaServer,Pages,servlets">
<META N A M E = " d e s c r i p t i o n "
CONTENT="A q u i c k e x a m p l e of JSP e x p r e s s i o n s . " >
<LINK REL=STYLESHEET
HREF="JSP-Styles . e s s "
TYPE="text/css">
</HEAD>
<BODY>
<H2>JSP E x p r e s s i o n s < / H 2 >
<UL>
< L I > C u r r e n t t i m e : <%= new j a v a . u t i l . D a t e ( ) %>
<LI>Your h o s t n a m e : <%= r e c j u e s t . g e t R e m o t e H o s t ( ) %>
<LI>Your s e s s i o n ID: <%= s e s s i o n . g e t l d O %>
<LI>The <CODE>testParam</CODE> form p a r a m e t e r :
<%= r e q u e s t . g e t P a r a m e t e r ( " t e s t P a r a m " ) %>
</UL>
</BODY>
</HTML>
908 Глава 2 0 . JavaServer Pages

£te i * У « « я о Convaunicato»

; ^^ ^ 3 й J>!t i ^ ^:f rf Q^ ^Й Щ

JSP Expressions
• Current time: Mon Aug 07 15:06:27 EDT 2000
• Your hostname: 127.0.0,1
• Your session Ш: Tol010mC22565256379010112At
• The t e s t P a r am form parameter: null
Рис. 20.1. Результаты обращения
'^ - i i -Л.. к Expressions.jsp

Скриптлеты
Если вы хотите выполнить действия более сложные, чем включение выражения,
можете использовать скриптлеты. Скриптлеты позволяют помещать произвольный
Java-код в тело метода _ j s p S e r v i c e сервлета (вызываемого из метода s e r v i c e ) .
Скриптлеты задаются в следующем формате:
<% Java-код %>
Скриптлетам, как и выражениям, доступны предопределенные переменные
( r e q u e s t , r e s p o n s e , s e s s i o n , o u t и т.д.). Так, если вы хотите, чтобы некоторые
данные появились в составе результирующей страницы, можете воспользоваться пе­
ременной o u t .
<%
String queryData = request.getQueryString();
out.println("Attached GET data: " + queryData);
%>
В данном конкретном примере можно добиться тех же результатов гораздо проще,
используя след}^ющее выражение:
A t t a c h e d GET d a t a : <%= r e q u e s t . g e t Q u e r y S t r i n g ( ) %>
Однако в целом с к р и т л е т ы являются более универсальным средством и мог)^ вы­
полнять задачи, недоступные выражениям. В качестве примера таких задач можно
привести установку кода состояния и полей заголовка ответа, обеспечение побочных
эффектов, таких как запись данных в файл протокола или обновление базы данных,
организация циклов, выполнение условных выражений и т.д. Например, следующая
строка кода указывает, что документ, передаваемый клиенту, является текстовым до­
кументом и в нем отсутствуют HTML-дескрипторы:
<% r e s p o n s e . s e t C o n t e n t T y p e ( " t e x t / p l a i n " ) ; %>
Заметьте, что устанавливать код состояния и поля заголовка можно в любой части
JSP, даже если на первый взгляд это выглядит как нарушение правила (согласно кото­
рому поля заголовка должны быть установлены до начала передачи содержимого до­
кумента). Это допустимо потому, что в JSP используется специальный тип потока
( J s p W r i t e r ) , выполняющий частичную буферизацию документа. Возможности бу­
феризации могут быть изменены; подробнее об этом разговор пойдет в разделе 20.4.
20.3. Элементы сценариев JSP 909

Пример включения в состав JSP фрагмента кода, более сложного, чем JSP-
выражение, приведен в листинге 20.2. В данном случае для установки цвета фона ис­
пользуется значение параметра bgColor, переданного в составе запроса. Результаты
обращения в данному JSP показаны на рис. 20.2 и 20.3.

Листинг 20.2. BGColor.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Color Testing</TITLE>
</HEAD>
<%
String bgColor = request.getParameter("bgColor");
boolean hasExplicitColor = true;
if (bgColor == null) {
hasExplicitColor = false;
bgColor = "WHITE";
}
%>
<BODY BGCOLOR="<%= bgColor %>">
<H2 ALIGN="CENTER">Color Testing</H2>
<%
if (hasExplicitColor) {
out.println("You supplied an explicit background color of " +
bgColor + ".");
} else {
out.println("Using default background color of WHITE. " +
"Supply the bgColor request attribute to try " +
"a standard color or RRGGBB value, or to see " +
"if your browser supports Xll color names.");
}
%>
</BODY>
</HTML>

|.y.'P|jlffl|Wi|i.i.l|Jl.l|.!I^I^IIJ.!JIJi].!).l|Ji|l
Fiis £di' \V4-v Fiivortes Joois Help

file Edft View F^rrtaftes Xods He^

1] ; AfiMw*» 1 ^ NtpV/localhost/cwp/BgColof |sp'?bgColoi=COCOCO


"3
Asl*est jig] hKp ///localhost/cwp/'BgCoioi |sp "13 ^
Color Testing Color Testing
Using default background color of "WHTTE. Supply the bgColor You supplied axi exphcit background c'.;!or of COCOCO.
request attnbute to try a standard color or KE.GGBB value, or to
see if your browser supports XI1 color names
J: J
l^jOone f si Local TiistvA g ] DGr« -h.lvAriV'Xr^

Рис. 20.2. Выходные данные, генерируемые Рис. 20.3. Результаты обращения к BGColor.
BGColor. j sp по умолчанию j s p при использовании в качестве значения
параметра bgColor RGB-кода СОСОСО
910 Глава 2 0 . JavaServer Pages

Включение фрагментов JSP-файла в зависимости


от определенных условий
Скриптлеты можно использовать для включения стандартных языковых конст­
рукций HTML или JSP в зависимости от выполнения некоторых условий. Это воз­
можно потому, что статический HTML-текст и скриптлеты включаются в состав ме­
тода _ j s p S e r v i c e в том же порядке, в котором они появляются в составе JSP-
документа. Это означает, что скриптлет не обязательно должен представлять собой
завершенное выражение, а если блок остается открытым, статический HTML-текст
(преобразованный в вызов p r i n t l n ) или другие конструкции JSP становятся частью
этого блока. Рассмотрим следующий фрагмент JSP-документа:
<% i f (Math.random О < 0.5) { %>
Have а <B>nice</B> day!
<% } else { %>
Have a <B>lousy</B> day!
<% } %>
После преобразования соответствующий фрагмент скриптлета будет выглядеть
приблизительно следующим образом:
if (Math.random О < 0.5) {
out.println("Have a <B>nice</B> day!");
} else {
out.println("Have a <B>lousy</B> day!");
}

Специальные синтаксические конструкции


Чтобы успешно использовать скриптлеты, необходимо учитывать две особенно­
сти. Во-первых, если в скриптлете должна присутствовать пара символов '*%>", вместо
нее надо использовать последовательность **%\>". Во-вторых, выражение <% Java-код
%> можно представить в XML-формате. В этом случае оно будет выглядеть так:
<jsp:scriptlet>Java-кoл</jsp:scriptlet>
Эти два формата одинаково обрабатываются серверами, однако в спецификациях,
предшествующих JSP 1.2, поддержка j s p : s c r i p t l e t не была обязательным требова-

Декларации
Декларации JSP позволяют включать методы и поля в класс сервлета (за пределами
метода j s p S e r v i c e , вызываемого из метода s e r v i c e ) . Декларации задаются в сле­
дующем формате:
<%! Java-код %>
Поскольку декларации не генерируют выходные данные, они обычно применяются
в сочетании с JSP-выражениями или скриптлетами. Методы и поля, определяемые дек­
ларациями, используются в выражениях или скриптлетах. Не следует, однако, с помо­
щью деклараций пытаться переопределить методы, влияющие на жизненный цикл
сервлета ( s e r v i c e , d o G e t , i n i t и т.д.). Эти методы используются сервлетом, в кото-
2 0 . 3 . Э л е м е н т ы с ц е н а р и е в JSP 911

рый преобразуется JSP. Нет необходимости прибегать к услугам декларации, чтобы по­
лучить доступ к методу s e r v i c e , d o G e t или do P o s t , поскольку из тела s e r v i c e авто­
матически вызывается метод _ j s p S e r v i c e , в который помещается код, реализующий
выражения и скриптлеты. Для инициализации сервлета и освобождения занимаемых
ресурсов вы можете использовать методы j s p l n i t и j s p D e s t r o y . При выполнении
сервлета они обязательно будут вызваны из методов i n i t и d e s t r o y .
На з а м е т к у

Для инициализации JSP и освобождения занимаемых ресурсов ис­


пользуйте методы jsplnit и jspDestroy.

Ниже приведен фрагмент JSP-кода, который выводит число обращений к докумен­


ту с момента загрузки сервера (или с момента перезагрузки сервлета). Как вы помни­
те, при обращении нескольких клиентов к одному и тому же сервлету порождается
несколько потоков, в которых выполняется метод s e r v i c e одного экземпляра серв­
лета. Исключение могут составлять только сервлеты, реализующие интерфейс
S i n g l e T h r e a d M o d e l . Интерфейс S i n g l e T h r e a d M o d e l был описан в разделе 19.4 и
будет обсуждаться в разделе 20.4 при рассмотрении атрибута i s T h r e a d S a f e дирек­
тивы p a g e . Переменные экземпляра сервлета совместно используются несколькими
потоками, обрабатывающими различные запросы, поэтому переменная a c c e s s C o u n t
не должна быть объявлена как s t a t i c .
<%! p r i v a t e i n t a c c e s s C o u n t = 0; %>
Accesses t o page s i n c e s e r v e r r e b o o t :
<%= +-i-accessCount %>
Полностью код JSP представлен в листинге 20.3, а результаты выполнения показа­
ны на рис. 20.4.

Листинг 1 0 . 3 . A c c e s s C o u n t s . j s p

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>JSP D e c l a r a t i o n s < / T I T L E >
<META NAME="keywords"
CONTENT^"JSP,declarations,JavaServer,Pages,servlets">
<META N A M E = " d e s c r i p t i o n "
CONTENT="A q u i c k e x a m p l e of JSP d e c l a r a t i o n s . " >
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<H1>JSP Declarations</Hl>
<%? private int accessCount = 0 ; %>
<H2>Accesses to page since server reboot:
<%= ++accessCount %></H2>
</BODY>
</HTML>
912 Глава 2 0 . JavaServer Pages

^SZBB
ffc £ * t ^lew go jQomiTiuracator Ь ф

^: -/ 3 й ^^ Й ^л rf L^ Щ iS
^^^Bocfemafk* ^4i toc^^mjhtlp //localhost/'cwpAccessCounts jsp |^|

JSP Declarations
Accesses to page since server
reboot: 10
Рис. 20.4. Результаты очередного
; ^ b » ^ '
. >«;* '^^„ .ел \г^ обращения клиента к AccessCounts. j sp

Специальный синтаксис деклараций


Как и для скриптлетов, если вы хотите включить в состав декларации пару симво­
лов "%>", вы должны задать последовательность "% \ > " . XML-аналогом <% ! Java-код %>
является следующее выражение:
<j s p : d e c l a r a t i o n > J a v a - к о д < / j s p : d e c l a r a t i o n >

Предопределенные переменные
Для того чтобы упростить процесс создания JSP-выражений и скриптлетов, разра­
ботчику предоставляется ряд автоматически определяемых переменных, которые
иногда называют предопределенными объектами. Поскольку код, создаваемый на базе
деклараций, располагается за пределами метода _ j s p S e r v i c e , в декларациях эти пе­
ременные недоступны. К предопределенным переменным относятся r e q u e s t ,
r e s p o n s e , o u t , s e s s i o n , a p p l i c a t i o n , config,pageContext и page.

request
Данная переменная ссылается на объект H t t p S e r v l e t R e q u e s t , представляющий
запрос клиента. С ее помощью можно определить тип запроса (например, GET или
POST) или получить доступ к полям заголовка.

response
Переменная r e s p o n s e ссылается на объект H t t p S e r v l e t R e s p o n s e , связанный с
ответом, передаваемым клиенту. Поскольку выходной поток буферизуется (см.
описание переменной o u t ) , работая с JSP, можно устанавливать код состояния и
заголовки. Как вы знаете, в сервлетах эти действия допустимы только до начала
передачи данных клиенту.

out
Данная переменная предоставляет доступ к объекту P r i n t W r i t e r , который при­
меняется для передачи данных клиенту. Для того чтобы в JSP можно было рабо­
тать с переменной r e s p o n s e , используется вариант P r i n t W r i t e r с поддержкой
буферизации под названием J s p W r i t e r . Управлять размером буфера можно с по­
мощью атрибута b u f f e r директивы p a g e . Переменная o u t используется преиму-
20.4. Директива page 913

щественно в скриптлетах, поскольку результаты вычисления JSP-выражений авто­


матически помещаются в выходной поток и поэтому непосредственного обраще­
ния к o u t не происходит.

session
Данная переменная представляет объект H t t p S e s s i o n , связанный с запросом.
Объект сеанса создается автоматически, поэтому данная переменная доступна все­
гда, за исключением тех случаев, когда поддержка сеанса явно отключена с помо­
щью атрибута s e s s i o n директивы p a g e . Попытка обращения к переменной
s e s s i o n при отключенной поддержке сеанса приведет к возникновению ошибки
на этапе преобразования JSP в сервлет.

application
Переменная a p p l i c a t i o n ссылается на объект S e r v l e t C o n t e x t , полученный с
помощью вызова g e t S e r v l e t C o n t e x t . Сервлеты и JSP могут использовать объект
S e r v l e t C o n t e x t для постоянного хранения данных, вместо того, чтобы поме­
щать их в переменные экземпляра. В классе S e r v l e t C o n t e x t определены методы
s e t At t r i b u t e и g e t A t t r i b u t e , которые позволяют записывать произвольные
данные, связанные со специальными ключами. Разница между хранением инфор­
мации в переменных экземпляра и в объекте S e r v l e t C o n t e x t состоит в том, что
S e r v l e t C o n t e x t совместно используется всеми сервлетами, выполняющимися в
среде сервера (или в рамках Web-приложения).

config
Данная переменная представляет объект S e r v l e t C o n f i g для данного документа.

pageContext
В JSP определен новый класс с именем P a g e C o n t e x t , предназначенный для орга­
низации доступа к различным атрибутам документа и хранения разделяемых дан­
ных. Переменная p a g e C o n t e x t хранит ссылку на объект P a g e C o n t e x t , связан­
ный с текущей страницей.

page
Эта переменная является синонимом переменной t h i s и используется достаточно
редко. Предполагается, что она окажется полезной в будущем, когда в JSP будут
поддерживаться другие языки помимо Java.

20.4. Директива page


Директивы влияют на общую структуру сервлега, создаваемого на основании JSP-
документа. Формат директив представлен ниже. Двойные кавычки могут быть заме­
нены одинарными, но значение атрибута обязательно должно быть помещено либо в
двойные, либо в одинарные кавычки. Если кавычки должны присутствовать в составе
значения атрибута, они должны предваряться обратной косой чертой; вместо * надо
указывать \ ' , а вместо " — \ ".
914 Глава 20. JavaServer Pages

<%0 d i r e c t i v e a t t r i b u t e = " v a l u e " %>


<%0 d i r e c t i v e a t t r i b u t e l = " v a l u e l "
attribute2="value2"

a t t r i b u t e N = " v a l u e N " %>


В JSP поддерживаются т р и типа директив: p a g e , i n c l u d e и t a g l i b . Директива
p a g e позволяет контролировать структуру сервлета путем импортирования классов,
настройки суперкласса сервлета, установки типа содержимого и выполнения других
подобных действий. Директива p a g e может быть помещена в любом месте документа;
рассмотрению данной директивы и посвящен этот раздел. Вторая директива,
i n c l u d e , дает возможность включать файл в состав класса сервлета на этапе преоб­
разования JSP в сервлет. Эта директива должна помещаться в той точке документа, в
которую предполагается включить указанный файл. Подробно директива i n c l u d e
будет рассматриваться в разделе 20.5. В JSP 1.1 была определена еще одна директива,
t a g l i b , которая используется для определения новых дескрипторов. Эта директива
будет рассматриваться в разделе 20.7.
Директива p a g e позволяет использовать следующие атрибуты: i m p o r t , c o n t e n t T y p e ,
isThreadSafe, session, buffer, autoflush, extends, info, errorPage, isErrorPage и
l a n g u a g e . Эти атрибуты (зависящие от регистра символов) подробно описаны ниже.

Атрибут import
Атрибут i m p o r t директивы p a g e позволяет определять пакеты, импортируемые
сервлетом, в который преобразуется JSP-документ. Если вы явно не укажете импорти­
руемые классы, сервлет по умолчанию импортирует j a v a . l a n g . *, j a v a x . s e r v l e t . *,
j a v a x . s e r v l e t . j s p . *, j a v a x . s e r v l e t . h t t p . * и, возможно, некоторые пакеты,
специфические для конкретного сервера. Создавая JSP, следует явно импортировать
все пакеты, кроме указанных, поскольку при переходе на другой сервер набор паке­
тов, импортируемых по умолчанию, может измениться. Атрибут i m p o r t задается в
одном из двух следующих форматов:
<%@ p a g e i m p o r t = " p a c k a g e . c l a s s " %>
<%@ p a g e i m p o r t = " p a c k a g e . c l a s s l , . . . , p a c k a g e . c l a s s N " %>
Так, например, если в составе документа встречается приведенная ниже директи­
ва, это означает, что все классы, входящие в состав J a v a . u t i l , могут использоваться
без явного указания имени пакета.
<%@ p a g e i m p o r t = " J a v a . u t i l . * " %>
Атрибут i m p o r t — единственный из атрибутов директивы p a g e , который может
многократно использоваться в составе одного документа. Несмотря на то что дирек­
тиву p a g e разрешено включать в любую точку документа принято располагать выра­
жения i m p o r t в начале документа, либо, по крайней мере, перед первым использова­
нием класса, входящего в импортируемый пакет.
Заметьте, что разные серверы придерживаются различных правил относительно
того, где следует располагать определенные типы файлов классов. Например, при ра­
боте с Java Web Server 2.0 классы сервлетов помещаются в каталог s e r v l e t s, но при
этом классы, используемые сервлетами или JSP, должны находиться в каталоге
20.4. Директива page 915

c l a s s e s . JSWDK и Tomcat не налагают подобных ограничений. Конкретную инфор­


мацию по этому вопросу вы найдете в документации на сервер.
В листинге 20.4 показан код JSP-документа, использующего два класса, отличных
от стандартного набора, импортируемого JSP: j a v a . u t i l . D a t e и c w p . S e r v l e t -
U t i l i t i e s (см. листинг 19.21). Для того чтобы в ссылках на эти классы можно было
не указывать пакеты, применяется следующая директива:
<%@ p a g e i m p o r t = " J a v a . u t i l . * , c w p . * " %>
Ha рис. 20.5 и 20.6 показаны результаты обращений к данному JSP.

Листинг20.4. ImportAttribute. j s p

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>The i m p o r t A t t r i b u t e < / T I T L E >
<LINK REL=STYLESHEET
HREF="JSP-Styles.CSS"
TYPE="text/css">
</HEAD>
<BODY>
<H2>The i m p o r t A t t r i b u t e < / H 2 >
<%— JSP p a g e d i r e c t i v e —%>
<%(§ p a g e import»" j a v a . u t i l . * , c w p . *" %>
<%— Декларация —%>
<%!
p r i v a t e S t r i n g randomlDO {
i n t num = ( i n t ) ( M a t h . r a n d o m { ) * 1 0 0 0 0 0 0 0 . 0 ) ;
r e t u r n ( " i d " + num);
}
private final String NO_VALUE = "<I>No Value</I>";
%>
.<%-- Скриптлет --%>
<%
Cookie[] cookies = request.getCookies();
String oldlD =
ServletUtilities.getCookieValue(cookies, "userID", NO^VALUE);
if (oldlD.equals(NO_VALUE)) {
String newID = randomlDO;
Cookie cookie = new Cookie("userlD", newID);
response.addCookie(cookie);
}
%>
<%-- Выражения --%>
This page was accessed on <%= new Date() %> with a userlD
cookie of <%= oldlD %>.
</BODY>
</HTML>
916 Глава 2 0 . JavaServer Pages

оозв
Re 1<а Vww Fjvmites Jools МФ
Ещвавш^^вш
\ £fe £<St ^вл Fjvonle» Juofe Help

; ^' -* 'j^i a ta" "a j j ;3" а-^Ж^'-


_< АФ*е*$ I^JhUpT/iocalhost/cwp/lmportAUribute |sp j:^idres* 1 ^ Ntp //localhost/'cwp/'ImpoftAUribiJle |sp
d
"3 ^
The import Attribute The import Attribute
Tliis page TAa' s accessed on Mon Aiig 14 09 57 23 Tliis passe was accessed on Mon Aug 14 095 8 20
EDT 2000 with a iiserlD cookie of No lvalue. EDT 2000 witli a useilD cookie of i(fe590963
Л d
;€30one ^locaii*ar«t

Рис. 20.5. Результаты первого обращения Рис. 20.6. Результаты одного из последую­
к I m p o r t A t t r i b u t e . j sp щих обращений к I m p o r t A t t r i b u t e . j sp

Атрибут contentType
Атрибут c o n t e n t T y p e позволяет задавать значение поля заголовка C o n t e n t -
Type, определяющее М1МЕ-тип документа, передаваемого клиенту. Дополнительную
информацию о MIME-типах вы найдете в разделе 19.10. Наиболее часто используемые
MIME-ТИПЫ перечислены в табл. 19.1.
Атрибут c o n t e n t T y p e задается в одном из следующих форматов:
<%@ page contentType="MIME-Type" %>
<%@ page contentType="MIME-Type; charset=Character-Set" %>
Например, директива
<%@ page contentType="application/vnd.ms-excel" %>

выполняет те же действия, что и следующий скриптлет:


<% response.setContentType("application/vnd.ms-excel") %>
В отличие от сервлетов, где по умолчанию предполагается М1МЕ-тип t e x t / p l a i n ,
для JSP устанавливается тип t e x t / h t m l (и набор символов ISO-8859-1).
В листинге 20.5 показан JSP-документ, генерирующий данные электронной таблицы,
где в качестве разделителя используются знаки табуляции. Н а рис. 20.7 показаны ре­
зультаты обращения к данному документу с помощью броузера Internet Explorer. Этот
броузер выполняется в операционной системе, где установлен пакет Microsoft Office.

Листинг 2 0 , 5 . E x c e l . j s p

First Last Email Address-


Marty Hall h a l l @ c o r e w e b p r o g r a r n m i n g . com
Larry Brown brown@corewebprogramming.com
Bill Gates gates@sun.com
Larry Ellison ellison@microsoft.com
<%@ p a g e c o n t e n t T y p e = " a p p l i c a t i o n / v n d . m s - e x c e l " %>
<%— T h e r e a r e t a b s , n o t s p a c e s , b e t w e e n c o l u m n s . —%>
20.4. Директива page 917

1 Be £d« У№^ |ns«ft Fgtmat loots |^^a go ^1^||Щ||

• ^ 3 4 aija:i3-J)-J mg.
Add»e«|#J http /Vlocaihosl/'cwp/Excei (Sfj
d
I P7C6 ^ «
1 ,^ _J_ 3„ _ 4 6__
Last Email Address
±:\
1 1 'First "
l_2_ Marty Hall halh^corewebprogramming.com """I
ГЗ"''Larry Brown brown@corewebprogrannmmg com |
1 4 J Bill Gates gates@sun com
j 5 Larry Ellison elliscn^micrcsoft com ^^ |
1И 4 • W \ E K c e l /
\*\ ! >\r\ Рис. 20.7. Документ Excel ( E x c e l . j sp),
отображаемый в броузере Internet Explorer

Атрибут isThreadSafe
Атрибут i s T h r e a d S a f e позволяет определить, должен ли сервлет, созданный на
основе JSP, реализовать и н т е р ф е й с S i n g l e T h r e a d M o d e l . Этот атрибут задается в
одном из двух форматов:
<%0 p a g e i s T h r e a d S a f e = " t r u e " %> <%-- По умолчанию —%>
<%@ p a g e i s T h r e a d S a f e = " f a l s e " %>
В обычных сервлетах каждый новый запрос выполняется в отдельном потоке. П р и
этом предполагается, что разработчиком сервлета приняты меры, необходимые для
нормальной обработки разделяемых данных разными потоками. К таким мерам отно­
сится синхронизация доступа к данным, позволяющая исключить их разрушение из-за
одновременного обращения из различных потоков. В одних сл}^чаях (как, например,
при подсчете посетителей) не стоит беспокоиться о том, что два клиента могут полу­
чить одно и то же значение, в других случаях (например, при определении иденти­
фикаторов пользователей) тот же результат означает грубую ошибку. Например, при­
веденный ниже фрагмент кода не обеспечивает корректную многопотоковую обра­
ботку, поскольку выполнение потока может быть прервано между чтением idNum и
записью нового значения, что приведет к появлению двух пользователей с одинако­
выми идентификаторами.
<%! private int idNum = 0; %>
<%
String userlD = "userlD" + idNum;
out.println ("Your ID is " 4- userlD + " . " ) ;
idNum = idNum + 1;
%>
Ч т о б ы данный фрагмент кода выполнялся корректно, его надо поместить в блок
synchronized.
synchronized(someObject) { }

В этом случае, если поток начал обработку блока, ни один другой поток не может
выполнить аналогичный блок кода до тех пор, пока первый поток не завершит работу
с синхронизированным фрагментом. П р и использовании блока s y n c h r o n i z e d при­
веденный выше фрагмент примет следующий вид:
<%! private int idNum = 0; %>
<%
918 Глава 20. JavaServer Pages

synchroni zed(thi s) {
String userlD = "userlD" + idNum;
out.println("Your ID is " + userlD + " . " ) ;
idNum = idNum + 1;
}
%>
Если же сервлет реализует интерфейс S i n g l e T h r e a d M o d e l , система гарантирует,
что один экземпляр сервлета не будет использован для одновременной обработки
двух различных запросов. Ч т о б ы обеспечить подобные условия, система либо создает
очередь запросов к одному экземпляру сервлета, либо создает пул экземпляров, каж­
дый из которых обслуживает отдельный запрос.
Задавая директиву <%@ p a g e i s T h r e a d S a f e = " f a l s e " %>, вы указываете на то,
что ваш код не обеспечивает корректную многопотоковую обработку, поэтому серв­
лет, созданный на основе JSP, должен реализовывать и н т е р ф е й с S i n g l e T h r e a d ­
Model. По умолчанию предполагается значение t r u e данного атрибута, т.е. сервлет
обеспечивает более высокую производительность за счет многопотоковой обработки
запросов в рамках одного экземпляра сервлета.

Атрибут session
Атрибут s e s s i o n указывает, должен ли данный документ поддерживать НТТР-
сеанс. Данный атрибут задается в одном из следующих форматов:
<%@ p a g e s e s s i o n = " t r u e " %> <%— По умолчанию --%>
<%@ p a g e s e s s i o n = " f a l s e " %>
Значение t r u e указывает на то, что предопределенную переменную s e s s i o n
(типа H t t p S e s s i o n ) необходимо связать с текущим объектом сеанса, если тот суще­
ствует. Если объект сеанса отсутствует, необходимо создать новый объект и связать
его с переменной s e s s i o n . Значение f a l s e указывает на то, что объект сеанса не
должен быть использован. В этом случае попытка обращения к переменной s e s s i o n
приведет к тому, что при преобразовании JSP в сервлет возникнет ошибка. Запрет
поддержки сеанса позволяет сэкономить память сервера. Следует помнить, что под­
держка сеанса ориентирована на пользователя, а не на документ. Поэтому нельзя от­
ключить поддержку сеанса для одного документа, не отключив его для других доку­
ментов, посещаемых тем же самым клиентом.

Атрибут buffег
Атрибут b u f f e r определяет размер буфера, используемого объектом, на который
ссылается предопределенная переменная o u t (обычно это экземпляр класса
J s p W r i t e r , являющегося подклассом класса P r i n t W r i t e r ) . Данный атрибут задается
в одном из следующих форматов:
<%@ p a g e b u f f e r = " p a 3 M e p k b " %>
<%@ p a g e b u f f e r = " n o n e " %>
Размер буфера, используемого сервлетом, может превышать заданный вами, но не
может быть меньше этого размера. Например, директива <%@ p a g e b u f f e r = " 3 2 k b "
20.4. Директива page 919

%> означает, что содержимое документа должно быть буферизовано и не будет от­
правлено клиенту до тех пор, пока объем данных, записанных в буфер, не достигнет
32 Кбайт, либо до тех пор, пока в буфер не будет помещен весь документ. Размер бу­
фера по умолчанию различается для разных серверов, но не бывает меньше 8 Кбайт.
Отключая буферизацию, соблюдайте осторожность. Если вы сделаете это, вам при­
дется следить за тем, чтобы код состояния и поля заголовка устанавливались в начале
файла, перед выводом содержимого HTML-документа.

Атрибут autoflush
Атрибут a u t o f l u s h позволяет указать, должно ли содержимое выходного буфера
автоматически передаваться по сети при его заполнении, либо при переполнении
буфера должно генерироваться исключение. Значение этого атрибута задается в од­
ном из следующих форматов:
<%(? p a g e a u t o f l u s h = " t r u e " %> <%— По умолчанию —%>
<%@ p a g e a u t o f l u s h = " f a l s e " %>
Если указан aTpH6yTbuf f e r = " n o n e " , значение f a l s e недопустимо.

Атрибут extends
Атрибут e x t e n d s описывает суперкласс сервлета, генерируемого на основе JSP, и
задается в следующем формате:
<%@ p a g e e x t e n d s = " p a c k a g e . c l a s s " %>
При использовании данного атрибута следует соблюдать осторожность, поскольку
сервер предполагает, что заданный класс будет выбран в качестве суперкласса сервлета.

Атрибут info
Атрибут i n f o определяет строку, возвращаемую при вызове метода g e t S e r v l e t -
I n f o сервлета. Формат данного атрибута приведен ниже.
<%@ p a g e i n f o = " Т е к с т сообщения" %>

Атрибут errorPage
Атрибут e r r o r P a g e задает JSP для обработки исключений (которые описываются
любым подклассом класса T h r o w a b l e ) , не обрабатываемых в рамках текущего доку­
мента. Значение атрибута задается в следующем формате:
<%@ p a g e errorPage="относигельный_(7/?Ь" %>
Объект исключения доступен JSP, указанному в качестве значения атрибута, по­
средством переменной e x c e p t i o n .
920 Глава 2 0 . JavaServer Pages

Атрибут isErrorPage
Данный атрибут позволяет определить, может ли текущий документ использо­
ваться другим JSP-документом для обработки исключений. Значение атрибута
i s E r r o r P a g e задается в одном из следующих форматов:
<%@ page isErrorPage="true" %>
<%@ page isErrorPage="false" %> <%-- По умолчанию — % >

Атрибут language
Предполагается, что в дальнейшем атрибут l a n g u a g e будет определять исполь­
зуемый язык программирования, например:
<%@ p a g e l a n g u a g e = " c o b o l " %>
В настоящее время этот атрибут не применяется, поскольку единственным допус­
тимым значением является значение Java по умолчанию.

Х1\/И'представление директив
Некоторые серверы позволяют использовать для директив XML-синтаксис. П р и
этом определение директивы принимает следующий вид:
< j s p : d i r e c t i v e . т и л _ л и р е к г и в ы атрибут="значение" />
Например, XML-эквивалент директивы
<%@ p a g e i m p o r t = " j a v a . u t i l . * " %>
выглядит следующим образом:
<jsp:directive.page import="Java.util.*" />

20.5. Включение файлов и аплетов в состав


JSP-документов
JSP предоставляет следующие средства для включения внешнего кода в состав до­
кумента.
L Директива include. Данная языковая конструкция позволят включать JSP-код в
состав документа перед тем, как он будет преобразован в сервлет. В состав вклю­
чаемого файла могут входить фрагменты кода, влияющие на JSP-документ в це­
лом, например определения переменных экземпляра или установка значения по­
ля C o n t e n t - T y p e . Данная директива будет обсуждаться ниже в этом разделе.
2. Действие jsp:include. Несмотря на то что повторно используемый JSP-код
предоставляет разработчику богатые возможности, иногда хочется пожертво­
вать их частью за право вносить небольшие изменения в состав включаемых
документов, не изменяя при этом основного кода JSP. Директива j s p : i n c l u d e
позволяет включать в состав JSP результаты обработки другого документа,
20.5. Включение файлов и аплетов в состав иЗР-документов 921

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


тивы i n c l u d e , код JSP не помещается в состав кода основного документа, по­
этому во включаемой странице не могут содержаться конструкции, влияющие
на состояние основного документа. Подробно действие j s p : i n c l u d e будет об­
суждаться далее в этом разделе.
3. Действие jspiplugin. Несмотря на то что данная глава посвящена средствам
Java, выполняемым на стороне сервера, Java-программы, предназначенные для
выполнения на стороне клиента и встраиваемые в Web-страницы, продолжают
играть важную роль, особенно в корпоративных сетях i n t r a n e t . Элемент
j s p i p l u g i n позволяет включать в состав JSP аплеты, использующие Java Plug-
In. Подробно данный вопрос рассматривается ниже в этом разделе.

Директива include: включение файлов на этапе


преобразования документа
Директива i n c l u d e может использоваться для включения файла в основной JSP-
документ. Включение выполняется на этапе преобразования основного документа в
сервлет (обычно преобразование выполняется при первом обращении клиента).
Данная директива задается в следующем формате:
<%@ i n c l u d e file="относительный_ияЬ" %>
Следует отметить, что включение файла выполняется при преобразовании JSP в
сервлет, а не при получении запроса клиента, как это происходит в случае
j s p : i n c l u d e . С таким поведением документа связаны две особенности.
Во-первых, включаемый файл может содержать JSP-код, влияющий на основной
документ в целом. Предположим, например, что файл s n i p p e t , j s p содержит сле­
дующую строку:
<%! int accessCount = 0 ; %>
В этом случае вы можете поступить таким образом:
<%@ i n c l u d e f i l e = " s n i p p e t . j s p " %> <%-- Определение a c c e s s C o u n t --%>
<%= a c c e s s C o u n t + + %> <%-- Использование a c c e s s C o u n t —%>
Во-вторых, при изменении включаемого файла все использующие его JSP-файлы
должны быть обновлены. Несмотря на то что серверы могут обнаружить изменения
включаемых файлов (и перекомпилировать сервлет), они не обязаны делать это, и на
практике данная возможность реализована в очень немногих серверах. JSP 1.1 позво­
ляет использовать параметр запроса j s p p r e c o m p i l e , который требует выполнить
преобразование JSP в сервлет. Однако в JSP 1.0 подобная возможность отсутствует.
Поэтому самый простой способ обеспечить повторное преобразование — изменить
дату модификации JSP-документа. Многие операционные системы позволяют сделать
это, не изменяя содержимое файла (в системе UNIX используется команда t o u c h ) .
Однако чаще всего для этой цели в начале документа помещают JSP-комментарии.
После модификации включаемого файла разработчик редактирует строку коммента­
риев. Например, вы можете указать в составе комментариев дату модификации вклю­
чаемого файла:
922 Глава 2 0 . JavaServer P a g e s

< % — Navbar.jsp modified 3/1/00 — % >


<%(§ include file="Navbar. jsp" %>

Внимание!!

Если вы внесли изменения во включаемый JSP-файл, необходимо


изменить дату модификации всех JSP-доку ментов, использующих
этот файл.

Например, в листинге 20.6 приведен JSP-файл, который предоставляет информа­


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

Листинг20.6. C o n t a c t S e c t i o n . j s p

<%@ p a g e i m p o r t = " J a v a . u t i l . D a t e " %>


<%— Последующий код б у д е т п р е о б р а з о в а н в команды с е р в л е т а ,
который с о з д а е т с я на б а з е J S P , включающего э т о т файл. --%>
<%!
p r i v a t e i n t accessCount = 0;
p r i v a t e Date accessDate = new D a t e ( ) ;
p r i v a t e S t r i n g accessHost = "<I>No p r e v i o u s a c c e s s < / I > " ;
%>
<P>
<HR>
T h i s p a g e &copy; 2001
<A HREF="http//www.my-company.com/">my-company.com</A>.
T h i s p a g e h a s b e e n a c c e s s e d <%= + + a c c e s s C o u n t %>
t i m e s s i n c e s e r v e r r e b o o t . I t was l a s t a c c e s s e d from
<%= a c c e s s H o s t %> a t <%= a c c e s s D a t e %>.
<% a c c e s s H o s t = r e q u e s t . g e t R e m o t e H o s t ( ) ; %>
<% a c c e s s D a t e = new D a t e O ; %>

Листинг 2 0 . 7 . S o m e R a n d o m P a g e . j s p

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>Some Random Page</TITLE>
<META NAME="author" CONTENT="J. Random H a c k e r " >
<META NAME="keywords"
CONTENT="foo,bar,baz,quux">
<META N A M E = " d e s c r i p t i o n "
CONTENT="Some random Web p a g e . " >
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
20.5. Включение файлов и аплетов в состав JSP-документов 923

<TABLE B0RDER=5 ALIGN="CENTER">


<TR><TH CLASS="TITLE">
Some Random Page</TABLE>
<P>
Information about our products and services.
<P>
Blah, blah, blah.
<P>
Yadda, yadda, yadda.
<%@ include file="ContactSection.jsp" %>
</BODY>
</HTML>

^o(iдf.!ДЯgШt^||J:iJЛJ!pлaLШ^!l^•li[p•pp•^ СбШ
fa« Edrt Ytew fit? iJommunJcflttof Йе1р ^

i ^'^eoofanoHcs Д t0cgtfton:|http//Jocalhost/cwp/SQmeRandomPagejsp Tjf^'V^af^Retated

Information about our products and services.

Blah, blah, blah.

Yadda, yadda, yadda.

Ibis page © 2001 mv-cornpanv.com. This page has been accessed 9 times since sorer
reboot. It was last accessed from localhost at Mon Apr 16 10:57:51 EDT 2001.
у «^^' 'Document Done

Рис. 20.8. Девятое обращение к SomeRandomPage . j sp

XML-представление директивы include


XML-эквивалент директивы
<%@ i n c l u d e f i l e = " . . . " %>
выглядит следующим образом:
<jsp:directive.include file="..." />

Включение файлов в момент запроса


Только что рассмотренная директива i n c l u d e позволяет включать реальный JSP-
код в состав различных документов, однако при внесении изменений во включаемый
файл необходимо модифицировать даты создания всех использующих его докумен­
тов. Это существенный недостаток данной директивы. Действие j s p : i n c l u d e по­
зволяет включать выходные данные JSP-файла, полученные в момент обращения кли­
ента к основному документу. Действие j s p : i n c l u d e не требует изменять дат)' созда­
ния JSP-файлов, но к моменту вызова подчиненного файла основной документ уже
924 Глава 2 0 . JavaServer P a g e s

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


дение основного документа. В большинстве случаев такое ограничение является
вполне приемлемым, поэтому действие j s p : i n c l u d e используется очень часто.
М е т о д и к а профессионалов

По возможности используйте для включения файлов действие


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

Несмотря на то что включаемые файлы не содержат JSP-код, ресурсы, используе­


мые при создании содержимого файла, могут быть JSP-документами. URL включаемо­
го ресурса интерпретируется сервером обычным образом, поэтому может указывать
на сервлет или JSP. Так действует метод method класса R e q u e s t D i s p a t c h e r , исполь­
зуемый сервлетом для включения файлов.
В элементе j s p : i n c l u d e должны содержаться два обязательных атрибута: p a g e
(относительный URL, который определяет включаемый файл) и f l u s h (вJSP 1.1 он
может не указываться, но если задан, то должен иметь значение t r u e ) :
< j s p : i n c l u d e раде="относительный_ияЬ" flush="true" />
В качестве примера рассмотрим страницу новостей, представленную в листинге
20.8. При модификации файлов I t e m l . h t m l , I t e m 2 . h t m l и I t e m 3 . h t m l основной
документ остается неизменным. Результаты обращения к документу показаны на
рис. 20.9.

Листинг 2 0 . 8 . WhatsNew . j s p

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>What's New a t JspNews.com</TITLE>
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<TABLE B0RDER=5 ALIGN="CENTER">
<TR><TH CLASS="TITLE">
W h a t ' s New a t JspNews.com</TABLE>
<P>
Here is a summary of our three most recent news stories:
<0L>
<LI><jsp:include page="news/lteml.html" flush="true" />
<LI><jsp:include page="news/ltem2.html" flush="true" />
<LI><jsp:include page="news/Item3.html" flush="true" />
</0L>
</BODY>
</HTML>
20.5. Включение файлов и аплетов в состав JSP-AOicyMeHTOB 925

Листинг 20.9. Iteml .html

<B>Bill Gates acts humble.</B> In a startling and unexpected


development, Microsoft big wig Bill Gates put on an open act of
humility yesterday.
<A HREF="http://www.microsoft.com/Never.html">More details...</A>

Листинг20.10. Item2.html

<B>Scott McNealy acts serious.</B> In an unexpected twist,


wisecracking Sun head Scott McNealy was sober and subdued at
yesterday's meeting.
<A HREF="http://www.sun.com/Imposter.html">More details...</A>

Листинг 20.11. Item3. html

<B>Larry Ellison acts conciliatory.</B> Catching his competitors


off guard yesterday, Oracle prez Larry Ellison referred to his
rivals in friendly and respectful terms.
<A HREF="http://www.oracle.com/Mistake.html">More details...</A>

^->','^.?П.'ПТ1ГЯ.'НЯВ

A ^ « f c s | e ] hUp //localhost/cwp/V/hatsNew |sp


"3
"3
iyVKafs NeWact J$pNews.coin
Here is a summary of our three most recent news stories:

1. ВШ Gates acts humble. In a startling and unexpected development, Microsoft


big wig ВШ Gates put on an open act of humility yesterday. More details...
2. Scott McNealy acts serious. In an unexpected twist, wisecracking Sun head
Scott McNealy was sober and subdued at yesterday's meeting. More details...
3. L a n y Ellison acts conc£liatoiy. Catching his competitors off guard yesterday,
Oracle prez Larry Hlison referred to his rivals in friendly and respectful terms
More details.
J
%Oci^ ;|^toca< intrant

Рис. 20.9. Включение файлов в момент запроса упрощает


процесс сопровождения документа

Включение аплетов, использующих Java Plug-In


Для включения обычных аплетов в JSP не требуются специальные синтаксические
конструкции, для этого достаточно использовать обычный HTML-дескриптор
APPLET. Если документ создается не для сети i n t r a n e t , в которой присутствуют
только броузеры Netscape 6, эти аплеты должны создаваться на базе JDK 1.1 или JDK
1.02, поскольку ни Netscape 4.x, ни Internet Explorer 5.x не поддерживают платформу
926 Глава 20. JavaServer Pages

Java 2 (т.е. JDK 1.2 или 1.3). Отсутствие такой поддержки накладывает на аплеты сле­
дующие ограничения.
• П р и необходимости использовать Swing надо копировать Swing-файлы по сети.
Этот процесс занимает много времени и приводит к ошибке при работе с
Internet Explorer 3 и Netscape 3.x и 4.01 — 4.05 (поскольку данные броузеры под­
держивают только J D K 1.02).
• В этих аплетах нельзя использовать Java 2D.
• Пакет c o l l e c t i o n s оказывается недоступным.
• Быстродействие аплета уменьшается, поскольку большинство компиляторов
для платформы Java 2 создают гораздо более э ф ф е к т и в н ы й код, чем их предше­
ственники.
Чтобы решить эти проблемы, компания Sun разработала дополнительный модуль
для Netscape и Internet Explorer, позволяющий использовать средства Java 2 при ра­
боте с различными броузерами. Java Plug-In можно найти по адресу h t t p : / / j a v a .
s u n . c o m / p r o d u c t s / p l u g i n / , кроме того, он входит в составJDK 1.2.2 и более позд­
них версий этого пакета. Поскольку размеры Java Plug-In составляют несколько мега­
байт, не стоит ожидать, что основная масса пользователей WWW установит на своих
компьютерах этот продукт. С другой стороны, такое решение оказывается вполне
приемлемым для корпоративных сетей intranet. В аплете можно предусмотреть авто­
матическую загрузку пакета в случае его отсутствия на каком-либо из компьютеров.
К сожалению, обычный дескриптор APPLET нельзя использовать с Java Plug-In, по­
скольку для обработки аплетов, включенных в документ с помощью этого дескриптора,
автоматически применяется встроенная виртуальная машина Java. Вместо этого при ра­
боте с Internet Explorer приходится использовать неудобные для восприятия дескрип­
торы OBJECT, а при работе с Netscape — дескрипторы EMBED. Более того, в большинстве
случаев разработчик документа не знает, какой именно броузер будет использован на
стороне клиента, поэтому ему приходится помещать в документ OBJECT и EMBED
(включать EMBED в раздел COMMENT дескриптора OBJECT) либо определять тип броузера
на основании данных запроса и соответственно изменять структуру документа. Это тре­
бует от разработчика затраты дополнительных усилий и времени.
Элемент j s p : p l u g i n позволяет создавать дескриптор для аплета, использующего
Java Plug-In. В различных серверах эта возможность реализуется по-разному, но в
большинстве случаев в состав сгенерированной Web-страницы включаются как
OBJECT, так и EMBED.

Элемент jsprplugin
Самый простой способ использования j s p : p l u g i n предполагает указание четы­
рех атрибутов: t y p e , c o d e , w i d t h и h e i g h t . В качестве значения атрибута t y p e зада­
ется a p p l e t , а остальные т р и атрибута используются так же, как и соответствующие
атрибуты дескриптора <APPLET>, с учетом следующих особенностей: имя атрибута
зависит от регистра символов, а значение всегда должно помещаться в одинарные
или двойные кавычки. Например, фрагмент кода
<APPLET CODE="MyApplet.class"
WIDTH=475 HEIGHT=350>
</APPLET>
20.5. Включение файлов и аплетов в состав JSP-документов 927

должен быть переписан следующим образом:


<jsp:plugin type="applet"
code="MyApplet.class"
width="475" height="350">
</jsp:plugin>
Элемент j s p i p l u g i n может содержать ряд необязательных атрибутов. Большин­
ство из них (но не все) выполняют те же функции, что и атрибуты элемента APPLET.
Допустимые атрибуты j s p : p l u g i n описаны ниже.
• type
В случае аплетов этот атрибут принимает значение a p p l e t . Однако Java Plug-In
также позволяет включить в состав Web-страниц элементы JavaBeans. В этом
случае значение данного дескриптора должно быть b e a n .
• code
Данный атрибут используется для той же цели, что и атрибут CODE дескрипто­
ра <APPLET>, т.е. определяет файл класса, являющийся подклассом A p p l e t или
JApplet.
• width
Данный атрибут используется аналогично атрибуту WIDTH дескриптора
<APPLET> и задает ширину (в пикселях) области, выделенной для аплета.
• height
Атрибут h e i g h t выполняет те же функции, что и атрибут HEIGHT дескриптора
<APPLET>, и задает высоту (в пикселях) области, выделенной для аплета.
• codebase
Данный атрибут имеет то же назначение, что и атрибут CODEBASE дескриптора
<APPLET>, т.е. задает базовый каталог для аплетов. В этом каталоге произво­
дится поиск файла, указанного посредством атрибута c o d e . Как и для элемента
APPLET, если этот атрибут не задан, используется каталог, в котором располо­
жен текущий документ. П р и работе JSP базовым считается каталог, в котором
находится исходный JSP-файл, а не тот каталог, в который помещается сгене­
рированный сервлет.
• align
Данный атрибут используется аналогично атрибуту ALIGN дескрипторов
<APPLET> и <IMG> и определяет тип выравнивания аплета. Допустимыми зна­
чениями являются l e f t , r i g h t , t o p , b o t t o m и m i d d l e .
• hspace
Данный атрибут используется для той же цели, что и атрибут HSPACE дескрип­
тора <APPLET>, т.е. определяет размеры пустого пространства в пикселях сле­
ва и справа от аплета.
• vspace
Указанный атрибут выполняет те же функции, что и атрибут VSPACE дескрип­
тора <APPLET>, т.е. определяет размеры пустого пространства в пикселях
сверху и снизу от аплета.
928 Глава 2 0 . JavaServer Pages

• archive
Атрибут a r c h i v e применяется аналогично атрибуту ARCHIVE дескриптора
<APPLET> и задает JAR-файл, из которого должны быть загружены классы и
изображения.
• name
Данный атрибут используется для той же цели, что и атрибут NAME дескрипто­
ра <APPLET>, т.е. определяет имя аплета. Это имя идентифицирует объект при
работе сценариев, написанных, например, на JavaScript.
• title
Атрибут t i t l e задает заголовок, предназначенный для отображения в качест­
ве подсказки либо для индексирования. Такое же назначение имеет редко ис­
пользуемый атрибут TITLE дескриптора <APPLET> (и многих других HTML-
элементов, определенных в HTML 4.0).
• j reversion
Данный атрибут определяет требуемую версию Java Runtime Environment
(JRE). По умолчанию принимается значение L L
• iepluginurl
Атрибут i e p l u g i n u r l задает URL Java Plug-In для Internet Explorer. Пользова­
телю, на компьютере которого дополнительный модуль отсутствует, будет
предложено скопировать его с указанного узла. П о умолчанию значение данно­
го атрибута ссылается на сервер узла Sun, но в сетя^ intranet можно организо­
вать копирование Plug-In с локального узла.
• nspluginurl
Атрибут n s p l u g i n u r l задает URL Java Plug-In для Netscape. По умолчанию
данный атрибут ссылается на сервер узла Sun, но в сетях intranet можно орга­
низовать копирование Plug-In с локального узла.

Элементы jsp:param и jsp:params


Элемент j s p : p a r a m используется совместно с j s p r p l u g i n так же, как элемент
PARAM используется с элементом APPLET, т.е. данный атрибут задает имя и значение
для обработки методом g e t P a r a m e t e r аплета. Однако j s p : p a r a m имеет две важные
особенности. Во-первых, поскольку j s p : p a r a m соответствует синтаксису XML, имена
атрибутов задаются символами нижнего регистра, значения помещаются в одинар­
ные либо двойные кавычки, а сам элемент должен заканчиваться не символом ">", а
последовательностью " / > " . Во-вторых, все вхождения j s p : p a r a m должны помещать­
ся между элементами j s p : p a r a m s .
Например, фрагмент
<APPLET C O D E = " M y A p p l e t . c l a s s "
WIDTH=475 HEIGHT=350>
<PARAM NAME="PARAM1" VALUE="VALUE1">
<PARAM NAME="PARAiyi2" VALUE="VALUE2">
</APPLET>
20.5. Включение файлов и аплетов в состав иЗР-документов 929

надо изменить следующим образом:


<jsp;plugin type="applet"
code="MyApplet.class"
width="475" height="350">
<jsp:params>
< j s p : p a r a m name="PARAMl" value="VALUEl" />
< j s p : p a r a m name="PARAM2" value="VALUE2" />
</jsp:params>
</jsp:plugin>

Элемент jsp: fallback


Данный элемент задает альтернативный текст для отображения в броузерах, не
поддерживающих элементы OBJECT и EMBED. Элемент j s p : f a l l b a c k используется
приблизительно так же, как и альтернативный текст элемента APPLET. Таким обра­
зом, фрагмент
<APPLET C O D E = " M y A p p l e t . c l a s s "
WIDTH=475 HEIGHT=350>
<B>Error: t h i s example r e q u i r e s Java.</B>
</APPLET>
необходимо переписать следующим образом:
<jsp:plugin type="applet"
code="MyApplet.class"
width="475" height="350">
<jsp:fallback>
<B>Error: this example requires Java.</B>
</jsp:fallback>
</jsp:plugin>

Пример использования jsp:plugin


в листинге 20.12 показан документ JSP, в котором элемент j s p : p l u g i n ссылается
на аплет P l u g i n A p p l e t . В листингах 20.13-20.15 представлен код аплета, а на рис
20.10 показаны результаты обращения к документу.

Листинг 2 0 . 1 2 . P l u g i n A p p l e t . j s p

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Using j s p : p l u g i n < / T I T L E >
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<TABLE B0RDER=5 ALIGN="CENTER">
<TR><TH CLASS="TITLE">
Using jsp:plugin</TABLE>
<P>
930 Глава 20. JavaServer Pages

<CENTER>
<jsp:plugin type="applet"
code="PluginApplet.class"
width="370" height="420">
</j sp:plugin>
</CENTER>
</BODY>
</HTML>

Листинг 20.13. PluginApplet. Java

import javax.swing.*;

/** Данный аплет использует Swing и Java 2D, поэтому для его
* выполнения требуется Java Plug-In.

public class PluginApplet extends JApplet {


public void initO {
WindowUtilities.setNativeLookAndFeel();
setContentPane(new TextPanel());

Листинг 20.14.TextPanel.Java

import java.awt.*;
import Java.awt.event.*;
import javax.swing.*;

/** Контейнер JPanel, который содержит панель с текстом,


* отображаемым под разными углами, и компонент JComboBox.

public class TextPanel extends JPanel


implements ActionListener {
private JComboBox fontBox;
private DrawingPanel drawingPanel;

public TextPanel0 {
GraphicsEnvironment env =
GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = env.getAvailableFontFamilyNames();
fontBox = new JComboBox(fontNames);
setLayout(new BorderLayout() ) ;
JPanel fontPanel = new JPanel();
fontPanel.add(new JLabel("Font:"));
fontPanel.add(fontBox);
JButton drawButton = new JButton("Draw");
drawButton.addActionListener(this);
fontPanel.add(drawButton) ;
20.5. Включение файлов и аплетов в состав JSP-документов 931

add(fontPanel, BorderLayout.SOUTH);
drawingPanel = new DrawingPanel();
fontBox.setSelectedltem("Serif");
drawingPanel.setFontName("Serif");
add(drawingPanel, BorderLayout.CENTER);
}

public void actionPerformed(ActionEvent e) {


drawingPanel.setFontName((String)fontBox.getSelectedItem()
drawingPanel.repaint();
}

Листинг 20.15.DrawingPanel.java

import java.awt.*;
import Java.awt.geom.*;
import javax.swing.*;

/** Окно, в котором текст отображается под некоторым углом.


* Для установки шрифта используется метод setFontName.
V
class DrawingPanel extends JPanel {
private Ellipse2D.Double circle =
new Ellipse2D.Double(10, 10, 350, 350);
private GradientPaint gradient =
new GradientPaint(0, 0, Color.red, 180, 180, Color.yellow,
true); // true означает повторение шаблона
private Color[] colors = { Color.white. Color.black };

public void paintComponent(Graphics g) {


super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setPaint(gradient);
g2d.fill(circle);
g2d.translate(185, 185);
for (int 1=0; i<16; i-f+) {
g2d.rotate(Math.PI/8.0);
g2d.setPaint(colors[i%2]);
g2d.drawstring("jsprplugin", 0, 0 ) ;
}
}
public void setFontName(String fontName) {
setFont(new Font(fontName, Font.BOLD, 35));
932 Глава 20. JavaServer Pages

Ш Uting itp-.phigin - Micn»<rft Internet Exploiei


Fife £,dit View F§vof*es Joofs hJelp

AiJdiew j ^ http //localhost/cwp/PiugirApplet |sp "3


"13
Using jsp:plug№

-* or

Рис. 2 0 . 1 0 . Результаты обращения


к документу P l u g i n A p p l e t . j sp
J посредством Internet Explorer. Ha данном
^ Applet sfaftP'd J k localirrfimeS
компьютере инсталлирован Java 2 Plug-In

20.6. Использование JavaBeans с JSP


JavaBeans API определяет стандартный ф о р м а т Java-классов. Визуальные средства
обработки и другие программы автоматически получают информацию об этих клас­
сах, а затем используют ее для создания объектов и выполнения различных действий
над ними; при этом пользователю не приходится специально заниматься написанием
соответствующего кода.
Подробное рассмотрение JavaBeans не входит в задачу данной книги. П р и необхо­
димости вы можете обратиться к многочисленным изданиям, посвященным этому во­
просу, либо к документации и учебным пособиям, находящимся по адресу
h t t p : / / j a v a . s u n . c o m / b e a n s / d o c s / . Для того чтобы понять материал данной гла­
вы, вам достаточно знать о JavaBeans след)^ющее.
1. Класс bean д о л ж е н включать конструктор, вызываемый без параметров.
Это требование можно обеспечить, либо определив такой конструктор явно,
либо не определяя другие конструкторы (в этом случае конструктор без пара­
метров будет создан автоматически). Данный конструктор вызывается при соз­
дании компонента JavaBean элементами JSP.
2. Класс bean не д о л ж е н содержать общедоступных (public) переменных эк­
земпляра. Доступ к переменным должен осуществляться с помощью специаль­
ных методов. Мы надеемся, что вы именно так и поступаете при написании
программ. Организация доступа к переменным посредством методов имеет
20.6. Использование JavaBeans с JSP 933

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


раничения на значения переменных (например, в объекте Саг, описывающем
движение автомобиля, применение метода s e t S p e e d позволяет запретить за­
давать отрицательные значения скорости). Во-вторых, при модификации
структуры данных и н т е р ф е й с остается неизменным. (Например, внутри класса
длина может представляться в ф)пгах либо в километрах; для других классов ос­
таются доступны методы g e t S p e e d l n M P H и g e t S p e e d l n K P H . ) В-третьих, если
изменение значения переменной должно вызывать некоторые побочные эф­
фекты, при использовании методов для доступа к переменным такое поведение
класса легко реализовать (например, вы можете обновлять изображение на эк­
ране при каждом вызове метода s e t P o s i t i o n ) .
3. Для обращения к постоянным значениям доллсны использоваться методы с
именами getXxxu setXxx. Например, если в классе Саг хршп^тся информация о чис­
ле пассажиров, вам след)^ет использовать метод g e t N u m P a s s e n g e r s (вызываемый
без параметров и возвращающий значение типа i n t ) и метод s e t N u m P a s s e n g e r s
(типа v o i d , при вызове которого передается целочисленный параметр). В этом
случае говорят, что класс Саг содержит свойство с именем N u m P a s s e n g e r s
(обратите внимание на то, что имя свойства начинается со строчной буквы; в сере­
дине имени могут присутствовать символы верхнего регистра). Если класс содер
жит метод qetXxx, но соответствующий метод s e t X r x отсутствует, считается, что
класс содержит свойство ххх, предназначенное только для чтения.
Единственным исключением из данного соглашения об именовании являются
методы, предназначенные для определения значений логических свойств. Они
имеют имена i s X x x Так, например, для чтения свойства l e a s e d в классе Саг
используется метод i s L e a s e d (типа b o o l e a n , который вызывается без пара­
метров), а для установки значения этого свойства— метод s e t L e a s e d (типа
v o i d , которому передается параметр b o o l e a n ) .
Несмотря на то что в скриптлетах и выражениях JSP можно вызвать любой ме­
тод класса, стандартные действия JSP позволяют использовать для обращения к
аплетам только методы g e t Ххх/ s e t Ххх или i s Ххх/ s e t Ххх.

Использование JavaBeans
Действие j s p : u s e B e a n позволяет загружать компонент b e a n для использования в
дoк)^мeнтeJSP. JavaBeans очень удобны, поскольку они позволяют повторно использо­
вать Java-классы, не нарушая основные принципы работы с JSP.
Приведенная ниже строка кода иллюстрирует простейший способ объявления
компонента b e a n .
<jsp:useBean id="name" c l a s s = " p a c k a g e . C l a s s " />
Такая конструкция означает: создать объект класса, указанного в качестве значе­
ния c l a s s , и связать его с переменной, имя которой задано как значение i d . Напри­
мер, следующее действие JSP
<jsp:useBean id="bookl" class="cwp.Book" />
может рассматриваться как аналог скриптлета:
934 Глава 20. JavaServer Pages

<% cwp.Book b o o k l = new c w p . B o o k O ; %>


Несмотря на то что действие j s p : u s e B e a n удобно рассматривать как аналог про­
цедуры создания объекта, оно предоставляет разработчику дополнительные возмож­
ности. Как вы увидите далее, вы можете применять атрибут s c o p e , что позволяет
связывать компонент b e a n не только с текущим, но и с другими документами. Для со­
вместного использования JavaBeans полезно иметь ссылку на существующие компо­
ненты, поэтому действие j s p : u s e B e a n предполагает, что новый экземпляр объекта
создается только в том случае, если не существует объекта с совпадающими значения­
ми i d и s c o p e .
Вместо атрибута c l a s s можно задавать beanName. Различие между ними состоит в
том, что beanName может ссылаться не только на класс, но и на файл, содержащий
сериал изованный объект. Значение атрибута beanName передается методу
i n s t a n t i a t e к л а с с а J a v a . b e a n s .Bean.
В большинстве случаев локальная переменная имеет тот же тип, что и создавае­
мый объект. Однако иногда удобнее, если для этой переменной объявлен тип, соот­
ветствующий суперклассу компонента b e a n или интерфейса, который этот объект
реализует. Для управления типом переменной используется атрибут t y p e , например:
<jsp:useBean i d = " t h r e a d l " class="MyClass" type="Runnable" />
Результаты выполнения этого кода аналогичны включению в тело метода
_ j s p S e r v i c e следующей команды:
R u n n a b l e t h r e a d l = new M y C l a s s O ;
Поскольку j s p : u s e B e a n использует XML-синтаксис, необходимо учитывать сле-
д)^ющие отличия от HTML: имена атрибутов чувствительны к регистру символов, зна­
чения должны быть помещены в одинарные или двойные кавычки и окончанием де­
скриптора является не символ ">", а пара "/>"• Первые две особенности относятся ко
всем JSP-элементам, которые имеют вид j s p : x x x . Третье отличие относится лишь к
тем элементам, в котором объединены открывающий и закрывающий дескрипторы.
Существуют также символы и символьные последовательности, для включения ко­
торых в состав значения атрибута необходимо принимать определенные меры. Так,
одинарная кавычка представляется двумя символами ( \ ' ) ' аналогично, двойная ка­
вычка предваряется обратной косой чертой ( \ " ) . Обратная косая черта представля­
ется как'ЛХ", вместо пары символов "%>" задается "%\>", а вместо '*<%" — "<\%".

Доступ к свойствам beans


Имея компонент b e a n , вы можете обращаться к его свойствам посредством дейст­
вия j s p : g e t P r o p e r t y . В составе j s p : g e t P r o p e r t y задаются атрибут паше, значе­
ние которого должно соответствовать атрибуту i d действия j s p : u s e B e a n , и атрибут
p r o p e r t y , определяющий имя требуемого свойства. Того же результата можно до­
биться, используя JSP-выражение, в котором непосредственно вызывается метод объ­
екта; ссылка на объект хранится в переменной, имя которой совпадает со значением
атрибута i d . Предположим, например, что в классе Book содержится свойство типа
S t r i n g с именем t i t l e и вы создали экземпляр класса b o o k l , воспользовавшись для
этого j s p : u s e B e a n . Включить в JSP-документ значение свойства t i t l e можно сле­
дующими способами:
20.6. Использование JavaBeans с JSP 935

< j s p : g e t P r o p e r t y name="bookl" p r o p e r t y = " t i t l e " />


<%= b o o k l . g e t T i t l e O %>
Первый подход считается более предпочтительным, поскольку таким способом мо­
гут воспользоваться разработчики Web-страниц, не имеющие опыта программирования
на Java. Однако непосредственное обращение к переменной также может оказаться по­
лезным, если, например, вам надо организовать цикл, использовать условное выраже­
ние либо вызвать метод, отличный от методов, представляющих значения свойств.
Те, кому незнакомы понятия свойств b e a n s , вместо интерпретации "данный компо­
нент содержит свойство типа Т с именем f оо" могут принять объяснение "данный класс
содержит метод с именем g e t Foe, который возвращает значение типа Т, кроме того, в
классе определен метод s e t F o o ; этому методу передается параметр типа Т, который со­
храняется в классе и возвращается при последующих вызовах метода g e t F o o " .

Простейший способ установки свойств beans


Для изменения значений свойств обычно используется j s p : s e t P r o p e r t y . Это
действие может задаваться по-разному. Самый простой формат предполагает наличие
трех атриб)'тов: name (его значение должно соответствовать атрибуту i d действия
j s p : u s e B e a n ) , p r o p e r t y (имя свойства, значение которого необходимо изменить) и
v a l u e (новое значение). Ниже в этом разделе мы представим альтернативные фор­
маты j s p : s e t P r o p e r t y , которые позволяют автоматически связывать свойства с
параметрами запроса. В данном разделе также рассказывается о значениях, вычис­
ляемых в момент получения запроса, и о соглашениях по преобразованию типов, ко­
торые позволяют задавать строковые значения для параметров, предполагающих по­
лучение целочисленных, символьных или логических данных.
Вместо действий j s p : s e t P r o p e r t y можно использовать скриптлет, который не­
посредственно вызывает методы объекта. Например, изменить значение свойства
t i t l e объекта b o o k l , рассмотренного ранее в этом разделе, можно одним из сле­
дующих двух способов:
<jsp:setProperty name="bookl"
property="title"
v a l u e = " C o r e Web Programming" / >
<% b o o k l . s e t T i t l e ( " C o r e Web P r o g r a m m i n g " ) ; %>
Действие j s p : s e t P r o p e r t y лучше подходит для разработчиков документов, не
имеющих опыта программирования, однако непосредственное обращение к объекту
позволяет выполнять более сложные операции, например устанавливать значение в
зависимости от выполнения некоторых условий либо вызывать методы, отличные от
методов getXxx и s e t X x x .

Пример класса StrmgBean


в листинге 20.16 приведен код простого класса с именем S t r i n g B e a n , располо­
женного в пакете cwp. Поскольку в классе отсутствуют общедоступные переменные
экземпляра и имеется конструктор, вызываемый без параметров, этот класс удовле­
творяет основным требованиям, предъявляемым к компонентам b e a n . Класс
S t r i n g B e a n содержит метод g e t M e s s a g e , возвращающий объект S t r i n g и метод
936 Глава 20. JavaServer Pages

setMessage, которому объект S t r i n g передается в качестве параметра. Согласно


терминологии JavaBeans, класс содержит свойство типа S t r i n g с именем message.
В листинге 20.17 показан JSP-файл, использующий класс StringBean. Экземпляр
S t r i n g B e a n создается с помощью действия j s p : useBean следующим образом:
<jsp:useBean i d = " s t r i n g B e a n " class="cwp.StringBean" />
После этого значение свойства message может быть включено в док)'мент одним
из следующих способов:
< j s p : g e t P r o p e r t y name="stringBean" property="message" />
<%= StringBean.getMessage() %>
Для модификации свойства message используются такие строки кода:
<j sp:setProperty name="stringBean"
property="message"
value="some message" />
<% StringBean.setMessage("some message"); %>
Результаты обращения к документу показаны на рис. 20.11.

Листинг 2 0 . 1 6 . S t r i n g B e a n . J a v a

package cwp;
/** Простой компонент bean, который содержит свойство типа
* String с именем message.
V
public class StringBean {
private String message = "No message specified";

public String getMessage() {


return(message);
}
public void setMessage(String message) {
this.message = message;

Листинг 20.17.StringBean.j sp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Using JavaBeans with JSP</TITLE>
<LINK REL=STYLESHEET
HREF="JSP-Styles.css"
TYPE="text/css">
</HEAD>
<BODY>
20.6. Использование JavaBeans с JSP 937

<TABLE B0RDER=5 ALIGN="CENTER">


<TR><TH CLASS="TITLE">
Using JavaBeans with JSP</TABLE>
<jsp:useBean id="stringBean" class="cwp.StringBean" />
<0L>
<LI>Initial value (getProperty):
<I><jsp:getProperty name="stringBean"
property="message" /></!>
<LI>Initial value (JSP expression):
<I><%= stringBean.getMessageO %></I>
<LI><jsp:setProperty name="stringBean"
property="message"
value="Best string bean: Fortex" />
Value after setting property with setProperty:
<I><j sp:getProperty name="stringBean"
property="message" /></!>
<LI><% StringBean.setMessage("My favorite: Kentucky Wonder"); %>
Value after setting property with scriptlet:
<I><%= StringBean.getMessageO %></I>
</0L>
</BODY>
</HTML>

ng iav^ti^n* mih JSP - Hetscapp


Fte £сй View i^o £or«Tiur»c<ato< У<ф

:"l : ^^ -h f£ Л :i;
,f wuc4rr'3fi<s ^' Localton jh'tp/.'localbost/с
1Д.
Using JavaBeans with JSP
1 bubal value (getProperty) No message specified
2. Initial value (JSP expression); No message specified
3. Value after setting property with setProperty: Best string bean: Fortex
4. Value after setting property with scnptlet: My favorite: Kentucky Wonder

Рис. 20.11. Результаты обращения


к документу StringBean.j sp

Установка свойств beans


Для установки свойств beans обычно используется действие j s p : s e t P r o p e r t y .
В простейшем виде данное действие содержит три атрибута: паше (значение которо­
го должно совпадать со значением атрибута i d действия j sp:useBean), p r o p e r t y
(имя свойства, значение которого необходимо изменить) и v a l u e (новое значение).
Например, в классе S a l e E n t r y , код которого показан в листинге 20.18, содержат­
ся свойства itemID (тип S t r i n g ) , numiterns (тип i n t ) , discountCode (тип double),
a также два свойства, предназначенных только для чтения, — itemCost и t o t a l C o s t
(каждое из них имеет тип double). В листинге 20.19 показан JSP-файл, в котором эк­
земпляр класса S a l e E n t r y создается след)'ющим образом:
<jsp:useBean i d = " e n t r y " c l a s s = " c w p . S a l e E n t r y " />
Результаты показаны на рис. 20.12.
938 Глава 20. JavaServer Pages

После создания экземпляра bean можно использовать входной параметр для уста­
новки значения свойства itemlD.
<jsp:setProperty
name="entry"
property="itemID"
value='<%= r e q u e s t . g e t P a r a m e t e r ( " i t e m I D " ) %>' />
Заметьте, что в качестве значения атрибута v a l u e используется JSP-выражение.
Для большинства атрибутов значения задаются в виде строковых литералов, однако
значения атрибутов v a l u e и name действия j s p : s e t P r o p e r t y могут вычисляться в
момент получения запроса. Если в составе выражения содержатся двойные кавычки,
вы можете поместить значение атрибута в одинарные кавычки либо указать перед со­
ответствующим символом обратную косую черту ( \ ' или \ " ) .

Листинг 2 0 . 1 8 . S a l e E n t r y . J a v a

package cwp;
/** Простой компонент bean, иллюстрирующий использование
* jsp:setProperty в различных формах.
Ч
public class SaleEntry {
private String itemID = "unknown";
private double discountCode = 1.0;
private int numltems = 0;

public String getltemlDO {


return(itemID);
}
public void setltemID(String itemID) {
if (itemID != null) {
this.itemID = itemID;
} else {
this.itemID = "unknown";
}
}

public double getDiscountCode() {


return(discountCode) ;
}
public void setDiscountCode(double discountCode) {
this.discountCode = discountCode;
}
public int getNumltems() {
return(numltems);
}
public void setNumltems(int numltems) {
this.numltems = numltems;
}
20.6. Использование JavaBeans с JSP 939

/ / в реальной программе следующий метод следует заменить


// поиском в базе данных.

public double getltemCost() {


double cost;
if (itemlD.equals("al234")) {
cost = 12.99*getDiscountCode0;
} else {
cost = -9999;
}
return(roundToPennies(cost)) ;
}
private double roundToPennies(double cost) {
return(Math.floor(cost*100)/100.0) ;
}
public double getTotalCost() {
return(getltemCost() * getNumltems());

Листинг 20.19.SaleEntryl.j sp

<1D0CTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Using jsp:setProperty</TITLE>
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<TABLE B0RDER=5 ALIGN="CENTER">
<TR><TH CLASS="TITLE">
Using jsp:setProperty</TABLE>
<jsp:useBean id="entry" class="cvф.SaleEntry" />
<j sp:setProperty
name=" entry "
property="i temID"
value='<%= request.getParameter("itemID") %>' />
<%
int numltemsOrdered = 1 ;
try {
numltemsOrdered =
Integer. parseint (request. getParameter ("niimltems ") ) ;
} catch(NumberFormatException nfe) {}
%>
<j sp:setProperty
name="entry"
property="numiterns"
value="<%= niomltemsOrdered %>" / >
940 Глава 2 0 . JavaServer Pages

<%
double discountCode = 1.0;
try {
String discountString =
request.getParameter("discountCode");
// Double.parseDouble недопустимо в JDK 1.1
discountCode =
Double.valueOf(discountString) doubleValue()
} catch(NumberFormatException nfe) {}
%>
<j sp:setProperty
name="entry"
property="discountCode"
value="<%= discountCode %>" />
<BR>
<TABLE ALIGN="CENTER" B0RDER=1>
<TR CLASS="COLORED">
<TH>Item ID<TH>Unit Price<TH>Number Ordered<TH>Total Price
<TR ALIGN="RIGHT">
<TD><jsp:getProperty name="entry" property="itemID" />
<TD>$<jsp:getProperty name="entry" property="itemCost" />
<TD><jsp:getProperty name="entry" property="numltems" />
<TD>$<jsp:getProperty name="entry" property="totalCost" />
</TABLE>
</BODY>
</HTML>

|.УШУМ, t^iiiiimifflB
fee E<irt View Fgvof^i*^ loo4 4?>lp

A^cfeess l ^ j h'tp //1ocalho5t/cwp/SaleEntfyl |sp'?i(enr»ID=a1234S.numi>ems=27SrdiscQuntCode=0 92 ; ^ |

"3
Usfna ISD:se№roDerty •

ВеюП) UmtT'iite КштяЪ*?! Onleie-sl Total Й1с<?


al234 J 11.95 2'? S32:. 6f.
J Рис. 20.12. Результат обращения к
^ D o f « ' ^ l o c a l ina«ie»
SaleEntryl.jsp

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


Установить значение свойства itemID не составляло труда, поскольку это свойст­
во имело значение типа S t r i n g . Выполнить ту же операцию со свойствами numltems
и d i s c o u n t C o d e несколько сложнее, так как они имеют числовые значения, в то
время как метод g e t P a r a m e t e r возвращает объект S t r i n g . Ниже представлен фраг­
мент кода, с помощью которого устанавливается значение numltems.

int numltemsOrdered = 1;
try {
numltemsOrdered =
Integer.parseInt(request.getParameter("numltems"));
20.6. Использование JavaBeans с JSP 941

} catch(NumberFormatException nfe) {}
%>
<jsp:setProperty
name="entry"
property="numItems"
value="<%= numltemsOrdered %>" />
К счастью, JSP предлагает изящное решение этой проблемы. Свойство можно свя­
зать с параметром запроса, автоматически преобразовывая строки символов в число­
вое, символьное или логическое значение. Вместо атрибута v a l u e задается атрибут
p a r a m , с помощью которого устанавливается имя входного параметра. Значение
входного параметра автоматически принимается в качестве значения свойства; при
необходимости выполняется преобразование типа. Если указанный параметр в за­
просе отсутствует, никакие действия не выполняются (система не присваивает свой­
ству значение n u l l ) . Так, фрагмент кода, предназначенный для установки значения
свойства n u m l t e r n s , сокращается до следующего действия:
<j sp:setProperty
name="entry"
property="numItems"
param="numlterns" />
В листинге 20.20 показан код JSP-документа, созданный с помощью предоставляе­
мой возможности.

Листинг 2 0 . 2 0 . S a l e E n t r y 2 . j s p

<jsp:useBean id="entry" class="cwp.SaleEntry" />


<jsp:setProperty
name="entry"
property="itemID"
param="itemID" />
<jsp:setProperty
name="entry"
property="numItems"
param="numlterns" />
<j sp:setProperty
name="entry"
property="discountCode"
param="discountCode" />

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


Если свойства объектов b e a n связываются со входными параметрами, система ав­
томатически преобразовывает типы для свойств, значениями которых являются про­
стые типы ( b y t e , i n t , d o u b l e и т.д.) и соответствующие им объекты ( ( B y t e , I n t e ­
g e r , D o u b l e и т.д.). Следует заметить, что в JSWDK 1.0.1 и Java Web Sei'ver 2.0 возни­
кает ошибка на этапе конвертирования в сервлет JSP-документа, в котором выполня­
ется автоматическое преобразование значений входных параметров в тип d o u b l e .
Tomcat и более новые серверы работают корректно.
942 Глава 2 0 . JavaServer Pages

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


Связывание свойства с входными параметрами избавляет разработчика от необ­
ходимости выполнения преобразований для многих простых встроенных типов.
Кроме того, JSP предоставляет разработчику дополнительную возможность, позволяя
связать со свойствами все входные параметры, которые имеют соответствующие
имена. Для этого достаточно задать значение "*" параметра p r o p e r t y . Таким обра­
зом, три действия j s p : s e t P r o p e r t y в листинге 20.20 можно заменить одной стро­
кой, приведенной ниже. Полностью кoдJSP представлен в листинге 20.21.
<jsp:setProperty name="entry" property="*" />
Пользуясь этим простым способом, необходимо учитывать следующие особенно­
сти. Во-первых, как и при установке значений отдельных свойств, если входной пара­
метр не найден, никакие действия не выполняются; система не устанавливает значе­
ние n u l l для данного свойства. Во-вторых, JSWDK и Java Web Server допускают ошиб­
ку при преобразовании входного параметра в значение d o u b l e свойства. В-третьих,
при автоматическом преобразовании типов нельзя принять эффективные меры для
фильтрации недопустимых значений и форматов. Поэтому при использовании авто­
матического преобразования типов есть шанс получить Web-страницу с сообщением
об ошибке. И, наконец, следует помнить, что имена свойств и входных параметров
должны совпадать с учетом регистра символов.

Листинг 2 0 . 2 1 . SaleEntiryS . j s p

< j s p : u s e B e a n i d = " e n t r y " c l a s s = " c w p , S a l e E n t r y " />


< j s p : s e t P r o p e r t y name="entry" p r o p e r t y = " * " / >

Совместное использование компонентов bean


До сих пор мы считали, что ссылки на объекты, созданные с помощью действия
j s p : u s e B e a n , хранятся в локальных переменных метода _ j s p S e r v i c e . Метод
_ j s p S e r v i c e вызывается из метода s e r v i c e сервлета, в который преобразуется JSP-
документ. Однако, в зависимости от атрибута s c o p e действия j s p i u s e B e a n , ссылка
на объект может быть организована по-разному. Допустимые значения атрибута
s c o p e перечислены ниже.
• page
Данное значение принимается по умолчанию. Оно указывает на то, что, помимо
связывания с локальной переменной, компонент b e a n должен быть помещен в
объект P a g e C o n t e x t , соответствующий текущем)^ запросу. Хранение компонента
в P a g e C o n t e x t означает, что сервлет может обращаться к нему, вызывая метод
g e t A t t r i b u t e с помощью предопределенной переменной p a g e C o n t e x t .
• application
Данное значение указывает на то, что, кроме связывания с локальной перемен­
ной, компонент b e a n помещается в разделяемый объект S e r v l e t C o n t e x t . Этот
объект доступен с помощью предопределенной переменной a p p l i c a t i o n либо
20.6. Использование JavaBeans с JSP 943

посредством вызова g e t S e r v l e t C o n t e x t . Объект S e r v l e t C o n t e x t совместно


используется всеми Web-приложениями (или, если Web-приложения не опреде­
лены, всеми сервлетами, выполняющимися в среде сервера). Значения, храня­
щиеся в S e r v l e t C o n t e x t , могут быть получены посредством метода g e t -
A t t r i b u t e . Разделение данных предоставляет следующие возможности.
Во-первых, разработчику предоставляются простые средства, позволяющие
организовать доступ нескольких сервлетов и JSP к одному и тому же объекту.
Подробно данный подход будет обсуждаться далее в этом разделе; там же будет
приведен пример.
Во-вторых, компоненты b e a n могут создаваться сервлетом, а не только исполь­
зоваться в JSP-документах. Это позволяет сервлету обрабатывать сложные за­
просы, устанавливая компоненты, помещая их в объект S e r v l e t C o n t e x t , а за­
тем перенаправляя запрос одному из JSP-документов. Подробно данный подход
будет обсуждаться в разделе 20.8.
• session
Данное значение указывает на то, что, помимо связывания с локальной пере­
менной, компонент b e a n должен помещаться в объект H t t p S e s s i o n , соответ­
ствующий тек)'щему сеансу. Для получения компонентов используется метод
getAttribute.
• request
Значение r e q u e s t указывает на то, что, помимо связывания с локальной пере­
менной, компонент b e a n должен помещаться в объект S e r v l e t R e q u e s t , отку-
да он может быть извлечен с помощью метода g e t A t t r i b u t e . Хранение зна­
чений в объекте запроса — т и п и ч н ы й подход при использовании архитектуры
МУС (Model 2). Подробно данный вопрос будет обсуждаться в разделе 20.8.

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


условий
Для более эффективного разделения данных компоненты bean создаются при вы­
полнении некоторых условий.
Во-первых, элемент j s p : u s e B e a n создает новый экземпляр bean только в том слу­
чае, если не существует компонент с совпадающими значениями i d и s c o p e . Если та­
кой компонент найден, он связывается с переменной, указанной посредством i d . Ес­
ли существующий b e a n представляет подкласс указанного класса, выполняется при­
ведение типов. Если приведение не может быть выполнено, генерируется
исключение C l a s s C a s t E x c e p t i o n .
Во-вторых, вместо
<jsp:useBean ... />
можно указать следующее действие:
< j s p : useBean . . . >БЬ/ражение</jsp: usеВеап>
Второй вариант элемента j s p : u s e B e a n отличается тем, что выражение, содер­
жащееся между открывающим и закрывающим дескрипторами, вычисляется только в
944 Глава 20. JavaServer Pages

том случае, когда создается новый компонент bean. При использовании существую­
щего bean выражение не вычисляется. Такое условное вычисление выражения удобно
в том случае, когда устанавливаются исходные свойства bean, совместно используе­
мые несколькими документами. Поскольку неизвестно, к какому документу клиент об­
ратится раньше других, вы не знаете, в какой документ следует поместить инициали­
зационный код. При использовании условного выражения инициализационный код
содержится во всех документах, но выполняется только один раз. Например, в лис­
тинге 20.22 приведен простой компонент bean, который подсчитывает обращения к
любому из связанных документов. В нем также хранится имя документа, обращение к
которому произошло раньше, чем к другим. В каждом из документов для поддержки
счетчика используется следующий фрагмент кода:
<jsp:useBean id="counter"
сlass="cwp.AccessCountBean"
scope="application">
< j s p : s e t P r o p e r t y narne="counter"
property="firstPage"
v a l u e = " C u r r e n t Page Name" />
</jsp:useBean>

C o l l e c t i v e l y , t h e pages u s i n g t h e c o u n t e r have been a c c e s s e d


< j s p : g e t P r o p e r t y n a m e = " c o u n t e r " p r o p e r t y = " a c c e s s C o u n t " />
times.
В листинге 20.23 показан один из трех JSP-документов, использующих счетчик.
Исходный код других документов можно найти по адресу h t t p : / / w w w . c o r e w e b p -
r o g r a m m i n g . com/. Типичный результат показан Fia рис. 20.13.

Листинг 2 0 . 2 2 . AccessCountBean . Java

p a c k a g e cwp;

/** Простой компонент bean, иллюстрирующий использование


* атрибута scope jspruseBean для разделения компонентов.
*/

public class AccessCountBean {


private String firstPage;
private int accessCount = 1;

public String getFirstPage() {


return(firstPage);

public void setFirstPage(String firstPage)


this.firstPage = firstPage;

public int getAccessCount() {


return(accessCount++);
20.6. Использование JavaBeans с JSP 945

Листинг 20.23.SharedCountsl.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Shared Access Counts: Page 1</TITLE>
<LINK REL=STYLESHEET
HREF-"JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<TABLE B0RDER=5 ALIGN="CENTER">
<TR><TH CLASS="TITLE">
Shared Access Counts: Page 1</TABLE>
<P>
<jsp:useBean id="counter"
class="cwp.AccessCountBean"
scope="application">
<jsp:setProperty name="counter"
property="firstPage"
value="SharedCountsl.jsp" />
</jsp:useBean>
Of SharedCountsl.jsp (this page),
<A HREF="SharedCounts2.j sp">SharedCounts2.j sp</A>, and
<A HREF="SharedCounts3.jsp">SharedCounts3.jsp</A>,
<jsp:getProperty name="counter" property="firstPage" />
was the first page accessed.
<P>
Collectively, the three pages have been accessed
<jsp:getProperty name="counter" property="accessCount" />
times.
</BODY>
</HTML>

l^Sfiafied Ассщщ Uamisi Р м в З «dtec^^p^l


fte Ш iiiw Ё0 iownuriK^w id«*P
Рис. 20.13. Результат обращения к
SharedCounts3 . j sp. В первую очередь
Shared Access Counts: Page 3 произошло обращение к документу
SharedCounts2 . j sp. Общее количество
Of SharcdCounts3 jsp (this page), L^harcdCoui-itcl pp. and визитов к документам
Shcu e iCoutit.^2 :sp. SharedCounts2 jsp was the first page accessed SharedCountsl.jsp,
SharedCounts2.j sp и
CoHecbvdy. the three pages have been accessed 13 times.
SharedCountsS.j sp,
предшествующих текущему,
]^H>"i voc'jKnsm -Jone равно 12
946 Глава 20. JavaServer Pages

20.7. Определение новых JSP-дескрипторов


JSP 1.1 предоставляет разработчикам возможность создавать собственные JSP-
дескрипторы, называемые пользовательскими дескрипторами. Вы определяете, как дол­
жен интерпретироваться сам дескриптор, его атрибуты и содержимое, и объединяете
созданные дескрипторы в наборы, называемые библиотеками дескрипторов. Эти
библиотеки могут использоваться в разных JSP-документах. Библиотеки дескрипто­
ров позволяют разработчикам реализовать достаточно сложный документ с помощью
нескольких элементов.
Создание новых дескрипторов преследует ту же цель, что и использование компо­
нентов bean, — реализовать сложное поведение документа посредством ограниченно­
го числа достаточно простых языковых конструкций. Однако между bean и пользова­
тельскими дескрипторами существуют различия. Во-первых, компоненты bean, в от­
личие от создаваемых дескрипторов, не могут изменять содержимое JSP. Во-вторых,
пользовательские дескрипторы позволяют свести сложные операции к значительно
более простым выражениям, чем это можно сделать, используя bean. В-третьих, для
определения пользовательского дескриптора требуется приложить значительно
большие усилия, чем для установки компонента bean. В-четвертых, компонент bean,
определенный в сервлете, может быть использован в других сервлетах или JSP-до­
кументах. Пользовательский дескриптор представляет собой более замкнутую конст­
рукцию. И, наконец, пользовательские дескрипторы можно применять только B J S P 1.1,
в то время как компоненты bean поддерживаются KaKjSP 1.0, так n J S P 1.1.

Компоненты, предназначенные для создания


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

Класс поддержки дескриптора


П р и определении нового дескриптора необходимо определить Java-класс, кото­
рый сообщает системе о том, какие действия она должна предпринять при появлении
данного дескриптора. Этот класс должен реализовывать и н т е р ф е й с j a v a x . s e r v l e t .
j s p . t a g e x t . T a g . Как правило, класс поддержки определяется как подкласс класса
T a g S u p p o r t или B o d y T a g S u p p o r t . В листинге 20.24 приведен пример простого де­
скриптора, который включает в состав JSP-документа строку символов "Custom t a g
e x a m p l e (cwp. t a g s . E x a m p l e T a g ) ". Если некоторые детали данного класса вам не­
понятны, не стоит беспокоиться: они будут подробно рассмотрены далее в этом раз­
деле. Сейчас же лишь заметьте, что данный класс с именем E x a m p l e T a g расположен в
пакете cv/p. t a g s . П р и работе с Tomcat 3 путь к данному файлу класса должен иметь
вид i n s t a l l _ _ d i r / w e b a p p s / R O O T / W E B - I N F / c l a s s e s / c w p / t a g s / E x a m p l e T a g . c l a s s .
20.7. Определение новых JSP-дескрипторов 947

Листинг 20.24. ExampleTag. java

package cwp.tags;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
/** Простой JSP-дескриптор, который включает в состав
* выходных данных строку ("Custom tag example...").
* Имя дескриптора здесь не определяется; это
* выполняется в файле описания библиотеки дескрипторов.
* Соответствующая ссылка оформляется с помощью директивы
* taglib в JSP-файле.
V
public class ExampleTag extends TagSupport {
public int doStartTag0 {
try {
JspWriter out = pageContext.getOut();
out.print("Custom tag example " +
"(cwp.tags.ExampleTag)");
} catch(lOException ioe) {
System.out.println("Error in ExampleTag: " + ioe);
}
return(SKIP_BODY);
}
}

Файл описания библиотеки дескрипторов


Определив класс поддержки дескриптора, необходимо идентифицировать его для
сервера и связать с конкретным XML-именем дескриптора. Эта задача выполняется с
помощью файла описания библиотеки дескрипторов (данный файл имеет XML-
формат). Пример такого файла приведен в листинге 20.25. Файл описания библиоте­
ки дескрипторов содержит некоторую постоянную информацию, имя и краткое опи­
сание библиотеки, а также набор описаний дескрипторов. Часть листинга, не выде­
ленная полужирным шрифтом, используется без изменений, поэтому ее можно вклю­
чать во все файлы описаний. Исходный код файла описания находится по адресу
http://www.corewebprogramming.com/, кроме того, он также входит в состав
стандартного набора примеров Tomcat 3 ( i n s t a l l _ d i r / w e b a p p s / e x a m p l e s / W E B -
INF/jsp).
Формат описания дескрипторов будет рассмотрен далее в этой главе. Сейчас же
заметьте, что элемент t a g определяет главное имя дескриптора (как вы увидите да­
лее, оно используется в качестве суффикса) и задает класс поддержки. Поскольку
класс поддержки дескриптора находится в пакете cwp. t a g s , полное имя класса имеет
вид cwp. t a g s . ExampleTag. Заметьте, что в элементе t a g приводится не URL или
относительный путь, а имя класса. Данный класс помещается в тот же каталог, где
расположены bean и другие вспомогательные классы. В Tomcat 3 стандартным распо­
ложением для таких классов является каталог install_dir/webapps/ROOT/WEB-
948 Глава 20. JavaServer Pages

I N F / c l a s s e s , поэтому E x a m p l e T a g будет помещен в каталог i n s t a l l _ d i r /


w e b a p p s / R O O T / W E B - I N F / c l a s s e s / c w p / t a g s . Размещение классов сервлетов в па­
кетах является признаком хорошего стиля программирования, а в Tomcat 3.1 это ста­
ло обязательным требованием.

Листинг 2 0 . 2 5 . c w p ~ t a g l i b . t l d

<?xml version="1.0" encoding="ISO-8859-l" ?>


<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://Java.sun.com/j2ee/dtds/web-jsptaglibrary_l_l.dtd">

<taglib>
<tlibversion>l.0</tlibversion>
<j spversion>l.1</jspversion>
<shortname>cwp</shortname>
<info>
A tag library from Core Web Programming Java 2 Edition,
http://www.corewebprogramming.com/.
</info>
<tag>
<name>example</name>
<tagclass>cwp.tags.ExampleTag</tagclass>
<info>Simplest example: inserts one line of output</info>
</tag>
</taglib>

JSP-файл
Определив класс поддержки дескриптора и файла описания библиотеки дескрип­
торов, вы можете приступать к написанию JSP-файла, содержащего пользовательский
дескриптор. П р и м е р такого файла приведен в листинге 20.26. Перед первым исполь­
зованием дескриптора необходимо включить директиву t a g l i b , которая представля­
ется в следующем формате:
<%@ t a g l i b u r i = " . . . " p r e f i x = " . . . " %>
Значением обязательного атрибута u r i может быть либо абсолютный, либо отно­
сительный URL файла описания библиотеки дескрипторов (пример такого файла
приведен в листинге 20.25).
Атрибут p r e f i x , который также является обязательным, задает префикс, который
указывается перед именем дескриптора, определенным в файле описания библиотеки
дескрипторов. Например, если в файле описания библиотеки дескрипторов опреде­
лен дескриптор с именем t a g l , а атрибут p r e f i x имеет значение t e s t , то реальным
именем дескриптора будет t e s t : t a g l . Данный дескриптор используется одним из
следующих способов:
<test:tagl>JSP-KOfl</test:tagl>
20.7. Определение новых JSP-дескрипторов 949

<test:tagl />
Файл описания, приведенный в листинге 20.25, называется c w p - t a g l i b . t l d и
находится в том ж е каталоге, ч т о и JSP-файл, код которого представлен в листинге
20.26. Поэтому директива t a g l i b , находящаяся в JSP-файле, содержит только имя
файла.
<%0 t a g l i b u r i = " c w p - t a g l i b . t l d " p r e f i x = " c w p " %>
Поскольку значение атрибута p r e f i x равно cwp, в остальной части JSP-документа
для ссылки на дескриптор e x a m p l e используется выражение cwp: e x a m p l e . Результа­
ты обращения к документу показаны на рис. 20.14.

Листинг 2 0 . 2 6 . SimpleExample. j s p

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<%@ t a g l i b u r i = " c w p - t a g l i b . t l d " p r e f i x = " c w p " %>
<TITLE><cwp:example /></TITLE>
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<Hl><cwp:example /></Hl>
<cwp:example />
</BODY>
</HTML>

ЕЖ
. lie
шаШ
S^^ )£tem F^vontes
^SM
loois Йе^
i,iriH!imiiff,mifffi-io|>^i

Ajjiew uB] fittp //lucalhcst/'cwp/TagFxamples/'SimpleEMmple |sp


"3
~1
Custom tag example
(cwp.tags.ExampleTag)
Custom tag example (cwp.tags ExampleTag)
J Рис. 20.14. Результаты обращения
Й ] Done ' f^ Locsi гт^егк*
к SimpleExample.j sp

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


в данном разделе описаны детали процесса создания простого дескриптора без
атрибутов, в теле которого не содержатся данные. Этот дескриптор имеет формат
<prefix:tagname />.
950 Глава 20. JavaServer Pages

Простой дескриптор: создание класса поддержки


Классы поддержки дескрипторов, в теле которых отсутствуют данные, должны
создаваться как подклассы класса TagSupport. Класс TagSupport содержится в паке­
те j avax. s e r v l e t . j s p . t a g e x t , реализует интерфейс Tag и обеспечивает основные
функции, необходимые для поддержки дескриптора. Поскольку в классе поддержки
используются другие классы, в нем обычно импортируются пакеты j a v a x . s e r v l e t .
j sp и j ava . i o . Выражения i m p o r t обычно имеют следующий вид:
import j a v a x . s e r v l e t . j s p . * ;
import j a v a x . s e r v l e t . j s p . t a g e x t . * ;
import j a v a . i o . * ;
Мы настоятельно рекомендуем вам скопировать код примера, обратившись по ад­
ресу http://www.corewebprograinming.com/, и использовать его в качестве
"стартовой точки" для дальнейшей работы.
Для дескрипторов без атриб)пгов и данных вы должны переопределить метод
d o S t a r t T a g . Если в документе присутствует открывающий дескриптор элемента,
этот метод вызывается в момент запроса. Для вывода данных метод должен получить
ссылку на объект J s p W r i t e r (разновидность потока P r i n t W r i t e r , доступная JSP-
документам посредством предопределенной переменной out). Это можно сделать с
помощью метода getOut класса PageContext, обратившись к нему с помощью пере­
менной pageContext. В дополнение к getOut в классе PageContext определены
также методы для получения данных, связанных с запросом. Наиболее часто исполь­
зуются g e t R e q u e s t , g e t R e s p o n s e , g e t S e r v l e t C o n t e x t и g e t S e s s i o n .
Поскольку метод p r i n t класса J s p W r i t e r может генерировать исключение
lOException, его вызов должен быть помещен в блок t r y / c a t c h . Для того чтобы
клиент получал сообщения о других типах ошибок, необходимо объявить, что при
выполнении метода d o S t a r t T a g может возникнуть исключение J s p E x c e p t i o n , и
генерировать его в случае ошибки.
Если тело дескриптора отсутствует, метод d o S t a r t T a g возвращает значение
SKIPBODY. Этим метод сообщает системе о том, что любые данные, содержащиеся
между открывающим и закрывающим дескрипторами, должны игнорироваться. В не­
которых случаях значение SKIP_BODY применяется и при наличии тела дескриптора,
однако создаваемый нами дескриптор имеет вид <pref i x : tagname /> и не предпо­
лагает поддержки данных.
В листинге 20.27 представлен код класса поддержки, который генерирует случай­
ные простые числа, состоящие из 50 цифр. Для генерации простых чисел использует­
ся класс Primes, который рассматривался в предыдущей главе.

Листинг 20.27.SimplePrimeTag.Java

package c w p . t a g s ;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.
import java.io.*;
import java.math.*;
import cwp.*;
2 0 . 7 . Определение новых JSP-дескрипторов 951

/** Generates а prime of approximately 50 digits. */

public class SimplePrimeTag extends TagSupport {


protected int len = 50;

public int doStartTagO {


try {
JspWriter out = pageContext.getOut();
Biglnteger prime = Primes.nextPrime(Primes.random(len));
out.print(prime);
} catch(lOException ice) {
System.out.println("Error generating prime: " + ioe);

return(SKIP BODY);
}
}

Простой дескриптор: создание файла описания


библиотеки дескрипторов
Файл описания библиотеки дескрипторов имеет простой формат и содержит
идентификатор версии XML, декларацию DOCTYPE и контейнер t a g l i b . Пример
файла можно найти по адресу h t t p : //www. corewebprogramming. com/. Для созда­
ния дескрипторов необходимо понимать структуру элемента t a g , содержащегося в
составе элемента t a g l i b . Для дескрипторов без атрибутов между <tag> и < / t a g >
должны содержаться следующие четыре элемента.
• name — в теле этого элемента определяется базовое имя дескриптора, которое
присоединяется к префиксу, заданному в составе директивы t a g l i b . В данном
примере мы задаем имя дескриптора s i m p l e Prime, т.е. используем следующее
выражение:
<name>simplePrime</name>
• tagclass — задает полное имя класса поддержки дескриптора. В данном случает
выражение выглядит так:
<tagclass>cwp.tags.SimplePrimeTag</tagclass>
• info — содержит краткое описание дескриптора. В нашем примере оно имеет
следующий вид:
< i n f o > O u t p u t s а random 5 0 - d i g i t prime.</info>
• bodycontent — в данном случае этот элемент может не указываться, но если он
включен, то для дескриптора, тело которого отсутствует, должно быть задано
значение empty. Для обычных дескрипторов, тело которых может интерпре­
тироваться, в составе элемента b o d y c o n t e n t задается значение JSP. В тех ред­
ких случаях, когда класс поддержки самостоятельно обрабатывает тело деск­
риптора, используется значение t a g d e p e n d e n t . Для рассматриваемого здесь
дескриптора SimplePrimeTag мы включаем в состав элемента b o d y c o n t e n t
значение empty.
952 Глава 2 0 . JavaServer P a g e s

<bodycontent>empty</bodycontent>
К сожалению, Tomcat 3.1 не поддерживает элемент bodycontent, и содержащий
его файл описания библиотеки дескрипторов не работает. Tomcat 3.2, JRun и
большинство других серверов корректно обрабатывают элемент b o d y c o n t e n t .
Внимание!!

Работая с сервером Tomcat 3.1, не используйте элемент bodycontent.

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

Листинг 20.28. c w p ~ t a g l i b . t l d (фрагмент)

<tag>
<name>simplePrime</naine>
<tagclass>cwp.tags.SimplePrimeTag</tagclass>
<info>Outputs a random 50-digit prime.</info>
</tag>

Простой дескриптор: создание JSP-файла


JSP-документ, в котором присутствует пользовательский дескриптор, должен вклю­
чать директиву t a g l i b , содержащую атрибут u r i , посредством которого указывается
расположение файла описания библиотеки, а также атрибут p r e f i x , значение кото­
рого предваряет основное имя дескриптора. В JSP-документе, код которого представ­
лен в листинге 20.29, директива t a g l i b имеет следующий вид:
<%@ t a g l i b u r i = " c w p - t a g l i b . t l d " prefix="cwp" %>
В ней указаны файл описания, фрагмент которого представлен в листинге 20.28, и
префикс cwp. Поскольку основное имя дескриптора simple Prime, в документе этот
дескриптор выглядит так:
<cwp:simplePrime />
Результаты обращения к документу показаны на рис. 20.15.

Листинг 20.29. SimplePrimeExample. j sp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Some 50-Digit Primes</TITLE>
<LINK REL=STYLESHEET
HREF="JSP-Styles.css"
TYPE="text/css">
</HEAD>
<BODY>
<Hl>Some 50-Digit Primes</Hl>
<%@ taglib uri="cwp-taglib.tld" prefix="cwp" %>
<UL>
20.7. Определение новых JSP-дескрипторов 953

<LI><cwp :simplePrime />


<LI><cwp :simplePrime />
<LI><cwp :simplePrime />
<LI><cwp : simplePrime />
</UL>
</BODY>
</HTML>

t-r4Mi,rH'Mi-i|IJil,l4M.I-l[if'!l
File £cM View £o £ommuf«cat« IHet

•^ ^^ 3 f^^ j^~ iS -^ d '3 l | SI


^^ ' Bockmatki ,^ Loc^bn Inttp //localhost/cwp/'TagExamples/SifnplePnmeExannpleisp -^

Some 50-Digit Primes


• 85525203807603181442992536487526237946236109329293
• 94866654313655363893733986180399466323070643332731
• 34410418758257965902314053586112120565127145728483
• 14536436218832698568115498683116771875392186140487
Рис. 20.15. Результаты обращения
;^Ь4^Г"" '^«:-,J'-» i:i_ -;^. к SimplePrimeExample.j sp

Поддержка атрибутов дескриптора


Если дескриптор может быть представлен в виде
<prefix:name a t t r i b u t e l = " v a l u e l " a t t r i b u t e 2 = " v a l u e 2 " . . . />
разработчик получает дополнительные возможности при создании библиотеки деск­
рипторов. Ниже описана реализация поддержки атрибутов дескрипторов.

Атрибуты: создание класса поддержки


Реализовать поддержку атрибутов достаточно просто. Использование атрибута с
именем a t t r i b u t e 1 предполагает наличие метода s e t A t t r i b u t e l в составе класса,
являющегося подклассом TagSupport (либо другим способом, реализующим интер­
фейс Tag). Значение атрибута передается методу в составе объекта S t r i n g . Таким
образом, поддержка атрибута с именем a t t r i b u t e 1 сводится к реализации следую­
щего метода:
p u b l i c void s e t A t t r i b u t e l ( S t r i n g v a l u e l ) {
doSomethingWith(valuel);

Заметьте, что атрибуту с именем a t t r i b u t e N a m e (строчная буква "а") соответст­


вует метод s e t A t t r i b u t e N a m e (прописная буква "А").
Одно из самых распространенных действий с атрибутами состоит в сохранении
его значения и последующем использовании в теле d o S t a r t T a g или другого метода.
Ниже представлен фрагмент кода, реализующий поддержку атрибута message.
private String message = "Default Message";
public void setMessage(String message) {
this.message = message;
954 Глава 2 0 . JavaServer Pages

Если к классу поддержки дескриптора могут обращаться другие классы, желатель­


но реализовать также метод g e t A t t r i b u t e N a m e . Метод s e t A t t r i b u t e N a m e являет­
ся обязательным.
В листинге 20.30 показан подкласс класса SimplePrimeTag, в котором добавлена
поддержка атрибута l e n g t h . Если этот атрибут присутствует в составе дескриптора,
вызывается метод s e t L e n g t h , который преобразует содержимое объекта S t r i n g в
целочисленное значение ( i n t ) и сохраняет его в переменной 1еп. Эта переменная
используется методом d o S t a r t T a g родительского класса.

Листинг 20.30. PrimeTag. Java

package c w p . t a g s ;
import javax.servlet.jsp.*;
import javax.servlet•j sp.tagext.*;
import java.io.*;

/** Генерирует случайное простое число, состоящее из N


* цифр (по умолчанию N = 50). Данный класс является
* подклассом SimplePrimeTag, в нем добавлен атрибут length,
* посредством которого задается размер простого числа.
* Метод doStartTag родительского класса использует поле
* 1еп для определения длины целого числа.

public class PrimeTag extends SimplePrimeTag {


public void setLength(String length) {
try {
len = Integer.parseint(length);
} catch (NiimberFormatException nf e) {
len = 5 0 ;
}
}

Атрибуты: создание файла описания библиотеки


дескрипторов
Атрибуты дескриптора объявляются в составе элемента t a g . Для этой цели служит
элемент a t t r i b u t e . Между < a t t r i b u t e > и < / a t t r i b u t e > помещаются следующие
элементы.
1. name — обязательный элемент, определяющий имя атрибута (зависящее от ре­
гистра символов). В нашем примере этот элемент имеет вид
<name>length</name>
2. required — обязательный элемент, который указывает, должен ли атрибут указы­
ваться всегда (true) или может отсутствовать (false). В нашем примере, поскольку
атрибут length не является обязательным, элемент r e q u i r e d выглядит так:
<required>false</required>
2 0 . 7 . Определение новых JSP-дескрипторов 955

Если атрибут в составе дескриптора отсутствует, метод s e t A t t r i b u t e N a m e не


вызывается, поэтом}^ для необязательных атрибутов надо предусмотреть зна­
чения по умолчанию.
3. rtexprvalue — необязательный элемент, который указывает, может ли значение
атрибута определяться с помощью JSP-выражения <%= e x p r e s s i o n %> ( t r u e )
либо должно быть задано в виде строки ( f a l s e ) . П о умолчанию принимается
f a l s e , поэтому, если вы не собираетесь определять значение атрибута в мо­
мент получения запроса, вы можете не указывать этот элемент.
В листинге 20.31 показан элемент t a g в составе файла описания библиотеки деск­
рипторов. Кроме элемента a t t r i b u t e , описывающего атрибут l e n g t h , в составе t a g
присутствуют также стандартные элементы name (значение p r i m e ) , t a g c l a s s
(значение cwp . t a g s . P r i m e T a g ) и i n f o (краткое описание).

Листинг 2 0 . 3 1 . c w p - t a g l i b . t l d (фрагмент)

<tag>
<name>prime</name>
<tagclass>cwp.tags.PrimeTag</tagclass>
<info>Outputs a random N-digit prime.</info>
<attribute>
<name>length</name>
<required>false</required>
</attribute>
</tag>

Атрибуты: создание JSP-фдйла


в листинге 20.32 показан JSP-документ, в котором содержится директива t a g l i b .
Эта директива задает файл описания библиотеки дескрипторов и префикс cwp. По­
скольку' в дескрипторе p r i m e может присутствовать атрибут l e n g t h , этот дескриптор
принимает следующий вид:
<cwp:prime l e n g t h = " x x x " />
Пользовательские дескрипторы создаются по правилам языка XML, поэтому зна­
чения атрибутов должны помещаться в одинарные или двойные кавычки. Поскольку
атрибут l e n g t h не является обязательным, дескриптор p r i m e может выглядеть так:
<cwp:prime />
В классе поддержки дескриптора должно быть предусмотрено значение по умол­
чанию, использующееся тогда, когда атрибут отсутствует в составе дескриптора. Ре­
зультаты обращения к документу, представленному в листинге 20.32, показаны на
рис. 20.16.
956 Глава 20. JavaServer Pages

Листинг 20.32.PrimeExample.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Some N-Digit Primes</TITLE>
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<Hl>Some N-Digit Primes</Hl>
<%@ taglib uri="cwp~taglib.tld" prefix="cwp" %>
<UL>
<LI>20-digit: <cwp:prime length="20" />
<LI>40-digit: <cwp:prime length="40" />
<LI>80-digit: <cwp:prime length=:"80" />
<LI>Default (50-digit): <cwp:prime />
</UL>
</BODY>
</HTML>

|[..f'l7M'i^»IWIfJII.:!J«!lfJI.U.!|l!.'IJI.Uiii|.![.]pi ЕШШ
Pe Edit View Favorites Toofs Hel?

.••^'•-- 'Jj2-:ii ^11^ -_•


• Adders ji5] http /'/localhost/'cwp/TagExamplei.'PtinrieExample isp
~3.
ij
Some N-Digit Primes
;; 32202893832239491679
• 20-cligit; 2
;: 6657206684091561727357099946005524053317
• 40-digit:^
. 80-digit:
41557069270593072733338187916203903965223542415375887442486332222481331561431357
. Default (50-cligit); 67648745674655715012021344027143131834812065849301
J
iS] Done -ф^ 1осЫ inhseieJ

Рис. 20.16. Результаты обращения к PrimeExample . j sp

Поддержка тела дескриптора


До сих пор мы обсуждали пользовательские дескрипторы, тело которых игнори­
ровалось. Эти дескрипторы задавались в следующем виде:
<prefix:tagname />
Сейчас мы рассмотрим, как определить дескриптор, тело которого должно обра­
батываться. Такие дескрипторы выглядят следующим образом:
< p r e f i x : t a g n a m e > г e л o д е с к ' р и п г о р а < / p r e f i x : tagname>
2 0 . 7 . Определение новых JSP-дескрипторов 957

Тело дескриптора: создание класса поддержки


в предыдущих примерах метод d o S t a r t T a g , определенный в классе поддержки
дескриптора, возвращал значение SKIP_BODY. Ч т о б ы сообщить системе о том, что
между открывающим и закрывающим дескрипторами могут присутствовать данные,
метод d o S t a r t T a g должен возвращать значение EVAL_BODY_INCLUDE. В теле деск­
риптора, как и в остальной части JSP-документа, могут содержаться элементы сцена­
риев, директивы и действия. JSP-конструкции преобразуются в сервлет, а затем вы­
полняются при получении запроса.
П р и наличии тела дескриптора вы, возможно, захотите, чтобы некоторые дейст­
вия выполнялись не только перед началом обработки данных, содержащихся между
открывающим и закрывающим дескрипторами, но и после окончания обработки. Эти
действия можно реализовать в методе doEndTag. Практически всегда после обработ­
ки тела дескриптора выполняется обработка остальной части документа. В этом слу­
чае метод d o E n d T a g возвращает значение EVAL_PAGE. Однако, если вы захотите пре­
рвать обработку, необходимо, чтобы метод возвращал значение SKIP_PAGE.
В листинге 20.33 определяется дескриптор, предназначенный для о ф о р м л е н и я за­
головков. Этот дескриптор обеспечивает значительно большую гибкость по сравне­
нию со стандартными HTML-элементами Н 1 - Н 6 . Н о в ы й элемент позволяет задавать
размер шрифта, список имен ш р и ф т о в для использования на стороне клиента, цвет
фона и переднего плана, обрамление и тип выравнивания (LEFT, CENTER, RIGHT). Из
всех этих возможностей в HTML-элементах Н 1 - Н 6 поддерживается только выравни­
вание. Заголовок реализуется с помощью таблицы, которая состоит из одной ячейки
и включает элемент SPAN, содержащий стили атрибутов. Метод d o S t a r t T a g генери­
рует открывающие дескрипторы <TABLE> и <SPAN> и возвращает значение
EVAL_BODY_INCLUDE, сообщая системе о том, что тело дескриптора должно быть об­
работано. Метод d o E n d T a g генерирует закрывающие дескрипторы </SPAN> и
</TABLE> и возвращает значение EVAL_PAGE, сообщающее о том, что обработка до­
кумента должна быть продолжена. Для поддержки атрибутов (например, b g C o l o r и
f o n t S i z e ) используются методы типа s e t A t t r i b u t e N a m e .

Листинг 2 0 . 3 3 . HeadingTag. j a v a

package cwp.tags;

import javax.servlet.jsp.*;
import j avax.servlet.j sp.tagext.*;
import java.io.*;

/** Генерирует HTML-заголовки, для которых указываются цвет


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

public class HeadingTag extends TagSupport {


private String bgColor; // Обязательный атрибут
private String color = null;
958 Глава 20. JavaServer Pages

private String align="CENTER";


private String fontSize="36";
private String fontList="Arial, Helvetica, sans-serif";
private String border="0";
private String width=null;

public void setBgColor(String bgColor) {


this.bgColor = bgColor;
}
public void setColor(String color) {
this.color = color;

public void setAlign(String align) {


this.align = align;
}
public void setFontSize(String fontSize) {
this.fontSize = fontSize;
}

public void setFontList(String fontList) {


this.fontList = fontList;
}
public void setBorder(String border) {
this.border = border;
}
public void setWidth(String width) {
this.width = width;
}
public int doStartTagO {
try {
JspWriter out = pageContext.getOut();
out.print("<TABLE BORDER=" + border +
" BGCOLOR=\"" + bgColor -f "\"" +
" ALIGN=\"" + align + " \ " " ) ;
if (width != null) {
out.print(" WIDTH=\"" + width + " \ " " ) ;
}
out.print("><TR><TH>") ;
out.print("<SPAN STYLE=\"" +
"font-size: " + fontSize + "px; " +
"font-family: " + fontList + "; " ) ;
if (color != null) {
out.println("color: " + color + " ; " ) ;
}
out.print("\"> " ) ; // Окончание <SPAN ...>
} catch(lOException ioe) {
System.out.println("Error in HeadingTag: " + ioe);
}
return(EVAL BODY INCLUDE); // Тело дескриптора обрабатывается
2 0 . 7 . Определение новых JSP-дескрипторов 959

public int doEndTagO {


try {
JspWriter out = pageContext.getOut0;
out. print ("</SPANX/TABLE>") ;
} catch(lOException ioe) {
System.out.println("Error in HeadingTag: " + ioe);
}
return(EVAL__PAGE); // Продолжение обработки JSP-документа
}

Тело дескриптора: создание файла описания библиотеки


дескрипторов
Для дескрипторов, предполагающих обработку содержимого, в составе элемента
tag появляется элемент b o d y c o n t e n t , содержащий значениеJSP.
<bodycontent>JSP</bodycontent>
Помните, однако, что сервер Tomcat 3.1 не поддерживает элемент b o d y c o n t e n t
и, поскольку этот элемент используется в основном для сред разработки, мы не вклю­
чили его в наш пример. Элементы паше, t a g c l a s s , i n f o и a t t r i b u t e используются
так же, как было описано ранее. Соответствующий фрагмент файла описания биб­
лиотеки дескрипторов представлен в листинге 20.34.

Листинг 20.34. c w p - t a g l i b . t l d (фрагмент)

<tag>
<name>heading</name>
<tagclass>cwp.tags.HeadingTag</tagclass>
<info>Outputs a 1-cell table used as a heading.</info>
<attribute>
<name>bgColor</name>
<required>true</required>
<!-- bgColor - обязательный атрибут -->
</attribute>
<attribute>
<name>color</name>
<required>false</required>
</attribute>
<attribute>
<name>align</name>
<required>false</required>
</attribute>
<attribute>
<name>fontSize</name>
<required>false</required>
</attribute>
<attribute>
<name>fontList</name>
<required>false</required>
</attribute>
960 Глава 20. JavaServer Pages

<attribute>
<name>border</name>
<required>false</required>
<./attribute>
<attribute>
<name>width</name>
<required>false</required>
</attribute>
</tag>

Тело дескриптора: создание JSP-файла


в листинге 20.35 показан документ, использующий определенный нами дескрип­
тор h e a d i n g . Поскольку атрибут b g C o l o r является обязательным, он присутствует во
всех вхождениях данного дескриптора. Результаты обращения к документу показаны
на рис. 20.17.

Листинг 2 0 . 3 5 . HeadingExample. j s p

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>Some T a g - G e n e r a t e d Headings</TITLE>
</HEAD>
<BODY>
<%@ t a g l i b u r i = " c w p - t a g l i b . t l d " p r e f i x = " c w p " %>
< c w p : h e a d i n g bgColor="#COCOCO">
Default Heading
</cwp:heading>
<P>
<cwp:heading bgColor="BLACK" color="WHITE">
White on Black Heading
</cwp:heading>
<P>
<cwp:heading bgColor="#EF8429" fontSize="60" border="5">
Large Bordered Heading
</cwp:heading>
<P>
<cwp:heading bgColor="CYAN" width="100%">
Heading with Full-Width Background
</cwp:heading>
<P>
<cwp:heading bgColor="CyAN" fontSize="60"
fontList="Brush Script MT, Times, serif">
Heading with Non-Standard Font
</cwp:heading>
</BODY>
</HTML>
20.7. Определение новых JSP-дескрипторов 961

a(ed Headings - Netscape

AH
<^f "Sookmoks j | , Locabonc|h«p //localhosl/cwp/TagExamples/HeadingExample |sp
"3
Default Heading
White on Blacic Heading

Lai^e Bordered Heading J


Heading with Full-Width Background

^ =Ф=' Ooctm»f<; Done jii ^ J'-» i:^) ^!^

PiAC. 20.17, Пользовательский элемент cwp: heading позволяет осуществлять


дополнительный контроль над выводом заголовков по сравнению со стандарт­
ными HTML-элементами Н1 — нб

Необязательные данные в составе дескриптора


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

Необязательное тело дескриптора: создание класса


поддержки
Если тело дескриптора может отсутствовать, то в зависимости от информации,
переданной в составе запроса, метод d o S t a r t T a g должен возвращать значение
EVAL_BODY_INCLUDE либо SKIP_BODY. Поскольку, в отличие от s e r v i c e , _ j s p -
S e r v i c e , d o G e t и d o P o s t , методу d o S t a r t T a g не передаются параметры
H t t p S e r v l e t R e q u e s t и H t t p S e r v l e t R e s p o n s e , возникает вопрос, как получить
данные запроса. Для доступа к информации, переданной в составе запроса, надо вы­
звать метод g e t R e q u e s t , обратившись для этого к переменной p a g e C o n t e x t , авто­
матически определяемой в классе T a g S u p p o r t , и получить объект H t t p S e r v l e t ­
R e q u e s t . Строго говоря, метод g e t R e q u e s t возвращает значение типа S e r v l e t -
R e q u e s t , поэтому, если вам нужен метод, специфический для класса H t t p S e r v l e t ­
R e q u e s t , необходимо выполнить приведение типов. Однако в данном случае нас ин­
тересует метод g e t P a r a m e t e r , поэтому приведение типов не требуется.
962 Глава 20. JavaServer Pages

В листинге 20.36 представлен код для дескриптора, тело которого обрабатывается


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

Листинг 20.36.DebugTag.Java

package cwp.tags;

import javax.servlet.jsp.*;
import javax.servlet.j sp.tagext.*;
import java.io.*;
import javax.servlet.*;

/** Дескриптор, содержимое которого обрабатывается, только


* если в составе запроса задан параметр debug.

public class DebugTag extends TagSupport {


public int doStartTagO {
ServletRequest request = pageContext.getRequest();
String debugFlag = request.getParameter("debug");
if ((debugFlag != null) &&
(!debugFlag.equalsIgnoreCase("false") ) ) {
return (EVAL_BODY_INCLUDE) ;
} else {
return(SKIP BODY);
}
}
}

Необязательное тело дескриптора: создание файла


описания библиотеки дескрипторов
Если пользовательский дескриптор может включать данные, в элементе
b o d y c o n t e n t задается значение JSP (если этот элемент используется). Другие компо­
ненты элемента t a g определяются так же, как и в предыдущих случаях. В листинге
20.37 приведена и н ф о р м а ц и я для дескриптора DebugTag.

Листинг 2 0 . 3 7 . c w p - t a g l i b . t l d (фрагмент)

<tag>
<name>debug</naine>
<tagclass>cwp.tags.DebugTag</tagclass>
<info>Includes body only if debug param is set.</info>
</tag>
20.7. Определение новых JSP-дескрипторов 963

Необязательное тело дескриптора: создание JSP-файла


в листинге 20.38 приведен код документа, в котором между дескрипторами
< c w p : d e b u g > и < / c w p : d e b u g > содержится отладочная информация. На рис. 20.18 и
20.19 показаны результаты обычного обращения к документу и ситуация, когда в со­
ставе запроса присутствует параметр d e b u g .

Листинг 2 0 . 3 8 . D e b u g E x a m p l e . j s p
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >
<HTML>
<HEAD>
<TITLE>Using t h e Debug Tag</TITLE>
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/ess">
</HEAD>
<BODY>
<Hl>Using t h e Debug Tag</Hl>
<%@ t a g l i b u r i = " c w p - t a g l i b . t l d " p r e f i x = " c w p " %>
Top of regular page. Blah, blah, blah. Yadda, yadda, yadda.
<P>
<cwp:debug>
<B>Debug:</B>
<UL>
<LI>Current time: <%= new java.util.Date() %>
<LI>Requesting hostname: <%= request.getRemoteHost() %>
<LI>Session ID: <%= session.getid() %>
</UL>
</cwp:debug>
<P>
Bottom of regular page. Blah, blah, blah. Yadda, yadda, yadda.
</BODY>
</HTML>

^< Using the Debug Tog - Netscape


£ile £dit View ^ o Communicator ti^lp

Ш
^'Booknriarks „4 Lgcattoft:|http.//localhQst/cwp/TeigExamples/DebugExamplejsp

Using t h e Debug T a g
Top of regular page. Blah, blah, blah. Yadda, yadda, yadda.

Bottom of regular page. Blah, blah, blah. Yadda, yadda, yadda.

y»;.^^: Document' Done ЛтШшШ&,М. ..Я:^..


Рис. 20.18. В обычных условиях тело элемента cwp: debug игнорируется
964 Глава 20. JavaServer Pages

f u s i n g theOebuq Tag-Netscape
£iie £dit ^iew Qo Communicator Oelp
^ ^ J Л j^. i^ ~.:J^ rf 2l j l SI
. ^ 1 " Bookmerks ^^ iQcatioajhttp //localhost/cwp/TegExamples/DebugExample )sp'?debug'tru~-^ ^ , Г Vvliat's Related

Using t h e Debug T a g
Top of regular page. Blah, blah, blah. Yadda, yadda, yadda.

Debug:

• Cun-ent time: Mon Apr 16 11:03:21 EDT 2001


• Requesting hostname: localhost
• Session Ш: Tol010mC6608409833952431At
Bottom of regular page. Blah, blah, blah. Yadda, yadda, yadda.
'^'r^ Document: Done

Рис. 20.19. При наличии параметра debug в составе запроса тело дескриптора
cwp: debug присутствует в составе ответа

Обработка тела дескриптора


в дескрипторе c w p : p r i m e данные отсутствовали, в дескрипторе c w p : h e a d i n g
всегда предполагалось наличие данных, а тело элемента cwp: d e b u g обрабатывалось
или игнорировалось в зависимости от параметров запроса. Общим для этих трех де­
скрипторов было то, что данные в их составе никогда не модифицировались; они ли­
бо игнорировались, либо использовались без изменений. В данном разделе мы пока­
жем вам, как происходит обработка тела дескриптора.

Обработка тела дескриптора: создание класса


поддержки
в предыд)^щих примерах классы поддержки дескрипторов создавались как под­
классы класса T a g S u p p o r t . Этот класс реализует и н т е р ф е й с Tag и выполняет необ­
ходимые операции, в частности, помещает в переменную p a g e C o n t e x t ссылку на
объект P a g e C o n t e x t . Однако возможности T a g S u p p o r t недостаточны для реализа­
ции тех дескрипторов, которые должны обрабатывать содержащиеся в них данные. В
этом случае вместо T a g S u p p o r t должен использоваться класс B o d y T a g S u p p o r t .
Класс B o d y T a g S u p p o r t является подклассом T a g S u p p o r t , поэтому методы
d o S t a r t T a g и d o E n d T a g могут использоваться так же, как и прежде. Кроме того, в
составе B o d y T a g S u p p o r t определены два новых метода.
1. doAfterBody — метод, который необходимо переопределить для выполнения
действий с данными в составе дескриптора. Обычно этот метод возвращает
значение SKIP_BODY, указывающее на то, что дальнейшая обработка тела деск­
риптора не требуется.
2. getBodyContent — метод, возвращающий объект B o d y C o n t e n t , в котором ин­
капсулирована информация о теле дескриптора.
20.7. Определение новых JSP-дескрипторов 965

В классе BodyContent содержатся три важных метода.


1. getEnclosingWriter — данный метод возвращает объект J s p W r i t e r , используе­
мый методами d o S t a r t T a g и doEndTag.
2. getReader — метод, возвращающий объект Reader, с помощью которого можно
читать данные в составе дескриптора.
3. getString— данный метод возвращает объект S t r i n g , содержащий тело деск­
риптора.

Класс S e r v l e t U t i l i t i e s (см. листинг 19.11) содержит статический метод f i l ­


t e r , который получает в качестве параметра строку текста и заменяет в ней символы
<, >, " и & последовательностями "&lt;", "&gt;", "&quot/" и "&amp;". Этот метод ис­
пользуется тогда, когда в составе строки, генерируемой сервлетом, могут содержаться
символы, нарушающие структуру HTML-документа. В листинге 20.39 показан код
класса, реализующего фильтрацию данных, содержащихся в составе пользовательско­
го JSP-дескриптора.

Листинг20.39. FilterTag. Java

package c w p . t a g s ;
import javax.servlet.jsp.*;
import j avax.servlet.j sp.tagext.*;
import java.io.*;
import cwp.*;

/** Преобразование символов <, >, " и & в специальные


* HTML-имена (&lt;, &gt;, &quot; и &amp;).
* После фильтрации строка символов может быть помещена
* в тело страницы либо использована как HTML-атрибут.

public class FilterTag extends BodyTagSupport {


public int doAfterBody{) {
BodyContent body = getBodyContent();
String filteredBody =
ServletUtilities.filter(body.getString());
try {
JspWriter out = body.getEnclosingWriter0;
out.print(filteredBody);
} catch(lOException ioe) {
System.out.println("Error in FilterTag: " + ioe);
}
// SKIP_BODY means we're done. If we wanted to evaluate
// and handle the body again, we'd return EVAL_BODY_TAG.
return (SKIP__BODY) ;
}
966 Глава 20. JavaServer Pages

Обработка тела дескриптора: создание файла описания


библиотеки дескрипторов
Если дескриптор должен обрабатывать данные, содержащиеся в нем, в файл описа­
ния библиотеки включается элемент b o d y c o n t e n t , который, так же, как и для случая
необрабатываемых данных, содержит значение JSP. Однако поскольку Tomcat 3.1 не
поддерживает b o d y c o n t e n t , при использовании данного сервера этот элемент может
не указываться. Остальные элементы используются так же, как и в предыдущих приме­
рах. В листинге 20.40 показан фрагмент файла описания библиотеки дескрипторов.

Листинг 2 0 . 4 0 . c w p - t a g l i b . t l d (фрагмент)

<tag>
<name>filter</name>
<tagclass>cwp.tags.FilterTag</tagclass>
<info>Replaces HTML-specific characters in body.</info>
</tag>

Обработка тела дескриптора: создание JSP-файла


в листинге 20.41 показан документ, содержащий таблицу, в которой представлены
примеры HTML-выражений и результаты их использования. Ч т о б ы создать такую
таблицу средствами HTML, необходимо затратить большие усилия, поскольку в левом
столбце таблицы необходимо представить символы "<" и ">" последовательностями
" & l t ; " и " & g t ; " . Задача еще более усложняется, если примеры должны часто изме­
няться. Упростить работу можно, применяя пользовательский дескриптор
<cwp : f i l t e r > . Код документа представлен в листинге 20.41, а результаты обращения
к нему показаны на рис. 20.20.

Листинг 2 0 . 4 1 . F i l t e r E x a m p l e . j s p

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>HTML Logical Character Styles</TITLE>
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<H1>HTML Logical Character Styles</Hl>
Physical character styles (B, I, etc.) are rendered consistently
in different browsers. Logical character styles, however,
may be rendered differently by different browsers.
Here's how your browser
(<%= request.getHeader("User-Agent") %>)
renders the HTML 4.0 logical character styles:
<P>
<%@ taglib uri="cwp-taglib.tld" prefix="cwp" %>
<TABLE B0RDER=1 ALIGN="CENTER">
20.7. Определение новых JSP-дескрипторов 967

<TR CLASS="COLORED"><TH>Example<TH>Result
<TR>
<TD><PRE><cwp:filter>
<EM>Some emphasized text.</EM><BR>
<STRONG>Some strongly emphasized text.</STRONG><BR>
<CODE>Some code.</CODE><BR>
<SAMP>Some sample text.</SAMP><BR>
<KBD>Some keyboard text.</KBD><BR>
<DFN>A term being defined.</DFN><BR>
<VAR>A variable.</VAR><BR>
<CITE>A citation or reference.</CITE>
</cwp:filter></PRE>
<TD>
<EM>Some emphasized text.</EM><BR>
<STRONG>Some strongly emphasized text.</STRONG><BR>
<CODE>Some code.</CODE><BR>
<SAMP>Some sample text.</SAMP><BR>
<KBD>Some keyboard text.</KBD><BR>
<DFN>A term being defined.</DFN><BR>
<VAR>A variable.</VAR><BR>
<C1TE>A citation or reference.</CITE>
</TABLE>
</BODY>
</HTML>

hl!lli'.llffHHiTffWnH|i|l41i'Hf#Hl

4^Ж:Ж.^..^..М.^.ЖШ.
^^йкюЫлк^ Д Lfieefiuft jhttp /'/'localhost/'cwp/TagExamples/'FikerExanfple |sp

HTML Logical Character Styles


Physical character styies (B, I, etc.) are rendered consistency in different browsers. Logical character stsdes,
however, may be rendered differently by different browsers. Here's how your browser (N1021113/4.7 [en] CWin98,
U)) renders the HTML 4 0 logical character styles

<EM>3oBfte e m p h a s i z e d t e x t . < / E M > < B R > ^^ , ^> *


<3TRONG>3ome s t r o n g l y e m p h a s i z e d text.</3TRONG><BR> „ ^ , , ., ,
kcODE>Some c o d e . </CODE><BR> |Some strongly empliasaed t e x t
iSome code.
;<3AMP>Some sample text.</3AMP><BR> Some seunple t e x t .
i<KBD>3ome keyboard text.</KBD><BR> Some k e ^ o a r d t e x t .
|<DFN>A term being defined.</DFN><BR> :A term being defined.
i<VAR>A variable.</VAR><BR> '^A variable
<CITE>A citation or reference.</CITE> A citation or reference

У*ИьГ ^it>owtt**l>or>» -щ^ ^p m, >j> ' i i


Рис. 20.20. Элемент cwp: f i l t e r позволяет включать в документ текст и не
заботиться при этом о замене специальных HTML-символов
968 Глава 20. JavaServer Pages

Многократная обработка тела дескриптора


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

Многократная обработка: создание класса поддержки


Если должна выполняться многократная обработка данных, содержащихся в со­
ставе дескриптора, необходимо создать класс поддержки, являющийся подклассом
BodyTagSupport. В классе, как и ранее, надо переопределить методы d o S t a r t T a g ,
doEndTag и, что очень важно, метод doAf terBody. Отличием от предыдущего при­
мера является значение, которое возвращает метод doAf terBody. Если этот метод
возвращает значение EVAL_BODY_TAG, тело дескриптора обрабатывается повторно, в
результате чего снова вызывается метод doAf terBody. Так продолжается до тех пор,
пока doAf terBody не вернет значение SKIP_BODY.
В листинге 20.42 показан код класса поддержки, в котором атрибут r e p s задает
число повторов тела дескриптора. Поскольку в теле дескриптора может содержаться
JSP-код (который конвертируется в код сервлета на этапе преобразования, но вызы­
вается в момент запроса), каждое новое повторение тела дескриптора не обязательно
должно приводить к отображению той же самой информации в окне клиента.

Листинг 20.42. RepeatTag. java

package c w p . t a g s ;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;

/** Дескриптор, для тела которого выполняется указанное


* число повторов.
V
public class RepeatTag extends BodyTagSupport {
private int reps;

public void setReps(String repeats) {


try {
reps = Integer.parseint(repeats);
} catch(NumberFormatException nfe) {
reps = 1;
}
}
public int doAfterBody() {
if (reps— >= 1) {
BodyContent body = getBodyContent();
try {
2 0 . 7 . Определение новых JSP-дескрипторов 969

JspWriter out = body.getEnclosingWriter();


out.println(body.getString{)) ;
body.clearBody(); // Очистка для последующих вычислений
} catch(lOException ioe) {
System.out.println("Error in RepeatTag: " + ioe);
}
return (EVAL_BODY_TAG) ;
} else {
return(SKIP^BODY);
}
}

Многократная обработка: создание файла описания


библиотеки дескрипторов
в листинге 20.43 приведен фрагмент файла описания библиотеки дескрипторов,
который присваивает рассматриваемому в данный момент дескриптору имя
cwp: r e p e a t . Чтобы разрешить поддержку значений атрибута, задаваемых при полу­
чении запроса, используется элемент r t e x p r v a l u e , содержащий значение t r u e .

Листинг 2 0 . 4 3 . c w p - t a g l i b . t l d (фрагмент)

<tag>
<name>repeat</name>
<tagclass>cwp.tags.RepeatTag</tagclass>
<info>Repeats body the specified number of times.</info>
<attribute>
<name>reps</name>
<required>true</required>
<!-- rtexprvalue указывает, может ли атрибут
представлять собой JSP-выражение. -->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

Многократная обработка: создание JSP-файла


в листинге 20.44 показан JSP-документ, который создает нумерованный список
простых чисел. Количество чисел задается с помощью параметра r e p e a t s , содержа­
щегося в составе запроса. Один из возможных результатов обращения к документу
показан на рис. 20.21.

Листинг 2 0 . 4 4 . R e p e a t E x a m p l e . j s p

<]DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Some 4 0 - D i g i t Primes</TITLE>
<LINK REL=STYLESHEET
970 Глава 2 0 . JavaServer Pages

HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<Hl>Some 40-Digit Primes</Hl>
Each entry in the following list is the first prime number
higher than a randomly selected 40-digit number.
<%@ taglib uri="cwp-taglib.tld" prefix="cwp" %>
<0L>
<!-- Repeats N times. A null reps value means repeat once. — >
<cwp:repeat reps='<%= request.getParameter("repeats") %>'>
<LI><cwp:prime length="40" />
</cwp:repeat>
</0L>
</BODY>
</HTML>

шззшшшяа miiffimniiii
&» i"^ V<e«^ F#wiJ*« look Ц ф

<^"^-<u-^ib-M^-^M-S\
I Afii^Si | € J h»tp //localhost/cwp/TagExamples/'RepeatExample |sp'^repeats=7 ^

"3
Some 40-Digit Primes
Each entry m the foflowing list is the first prime number higher than
a randomly selected 40-digit number,

1. 779042788484692290696496905990659223259
2. 5654911225096957336845971755232574136573
3. 4440250773563950124888671855331783776667
4. 4557511042237325396986257256572883741037
5. 584476361241995395252711786232246484577 Рис. 20.21. Результаты обращения к
6. 2878822708441674101514196529821727599631
7 9665985263198820747432458513545913598401
RepeatExample. j sp. Значение параметра
r e p e a t s , передаваемого в составе
^Ooiiei"" iltic4ir*t«ne запроса, равно 20

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


в листинге 20.44 элемент cwp: p r i m e находился внутри cwp: r e p e a t , однако эти
элементы не зависели один от другого. П е р в ы й из них, будучи включенным в любую
точку JSP-документа, генерирует простые числа, а второй организует повтор любых
данных, содержащихся между открывающим и закрывающим дескрипторами.
В отличие от рассмотренного примера, некоторые дескрипторы зависят от взаимного
расположения. Например, в языке HTML элементы TD и ТН могут находиться только в со­
ставе TR, а тот, в свою очередь, должен входить в состав элемента TABLE. Цвет и тип вы­
равнивания TABLE наследуются элементом TR, а установки TR, в свою очередь, влияют на
поведение TD и ТН. Таким образом, некоторые элементы, даже если их взаимное располо­
жение было выбрано правильно, зависят один от другого. Аналогично, в файле описания
библиотеки дескрипторов используются элементы t a g l i b , t a g , a t t r i b u t e и r e q u i r e d ,
порядок вложенности которых должен строго соблюдаться.
20.7. Определение новых JSP-дескрипторов 971

Ниже рассматривается процесс создания пользовательских дескрипторов, кото­


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

Вложенные дескрипторы: создание класса поддержки


Класс поддержки для вложенных дескрипторов должен создаваться как подкласс
класса T a g S u p p o r t либо класса B o d y T a g S u p p o r t . Выбор суперкласса зависит то то­
го, должно ли обрабатываться тело дескриптора (в этом случае выбирается супер­
класс B o d y T a g S u p p o r t ) , или (чаще всего) информация, содержащаяся в составе де­
скриптора, должна включаться без изменений (в этом случае выбирается суперкласс
TagSupport).
Существуют два основных подхода к определению вложенных дескрипторов. Во-
первых, вложенные дескрипторы могут использовать для поиска включающего деск­
риптора метод f i n d A n c e s t o r W i t h C l a s s . Этому методу передаются ссылка на теку­
щий класс (т.е. t h i s ) и объект C l a s s включающего класса. Если включающий класс
не найден, метод генерирует исключение J s p T a g E x c e p t i o n . Во-вторых, если один
дескриптор должен хранить данные для дальнейшего использования другим дескрип­
тором, он должен поместить эти данные в экземпляр включающего дескриптора. П р и
определении включающего дескриптора должны быть предусмотрены методы для за­
писи данных и доступа к ним.
Предположим, что мы собираемся определить набор дескрипторов, которые
должны использоваться следующим образом:
<cwp:if>
<cwp:condition><%= someExpression %></cwp:condition>
<cwp:then>JSP to include if condition is true</cwp:then>
<cwp:else>JSP to include if condition is false</cwp:else>
</cwp:if>
Чтобы выполнить эту задачу, надо сначала определить для дескриптора c w p : i f
класс поддержки I f Tag. Класс должен включать методы, определяющие, принимает
ли условие значение t r u e или f a l s e ( s e t C o n d i t i o n и g e t C o n d i t i o n ) , а также ме­
тоды для установки условия и проверки, было ли оно установлено ( s e t H a s C o n d i t i o n
и g e t H a s C o n d i t i o n ) . Это нужно для того, чтобы запретить дескрипторы cwp: i f , в
которых отсутствует выражение cwp: c o n d i t i o n . Код класса I f Tag показан в лис­
тинге 20.45.
Вторым этапом является определение класса поддержки для cwp: c o n d i t i o n .
В этом классе с именем I f C o n d i t i o n T a g определяется метод d o S t a r t T a g , который
проверяет, присутствует ли дескриптор в составе I f Tag. Если дескриптор обнаружен,
данный метод возвращает значение EVAL_BODY_TAG, в противном случае генерирует
исключение. Метод d o A f t e r B o d y класса поддержки ищет тело дескриптора
( g e t B o d y C o n t e n t ) , преобразует его в объект S t r i n g ( g e t S t r i n g ) и сравнивает со
значением t r u e . Данный подход позволяет в процессе разработки док)'мента заме­
нить выражение <%= e x p r e s s i o n %> значением t r u e , в результате чего будет вы­
полняться только компонент t h e n . Сравнение результата с t r u e означает, что любые
другие данные будут интерпретироваться как f a l s e . После того как сравнение вы­
полнено, результаты сохраняются во включающем дескрипторе, для чего использует­
ся метод s e t C o n d i t i o n класса I f T a g . Код класса I f C o n d i t i o n T a g представлен в
листинге 20.46.
972 Глава 20. JavaServer Pages

На третьем этапе определяется класс поддержки дескриптора cwp: then. Метод


doStartTag этого класса определяет, находится ли он в составе If Tag, а также про­
веряет выполнение некоторых условий (например, определяет, находится ли
I f Condi t i o n T a g в составе I f Tag). Метод doAfterBody проверяет, выполняется ли
условие в классе If Tag, и, если результат положителен, находит содержимое и выво­
дит его. Код класса представлен в листинге 20.47.
И, наконец, на последнем этапе определяется класс поддержки для cwp:else.
Этот класс действует аналогично классу поддержки компонента then, за исключени­
ем того, что doAfterBody выводит тело класса, если выражение во включающем
If Tag равно f a l s e . Код класса приведен в листинге 20.48.

Листинг 2 0 . 4 5 . I f T a g . j a v a

package c w p . t a g s ;
import j a v a x . s e r v l e t . j s p . * ;
import j a v a x . s e r v l e t . j s p . t a g e x t . * ;
import j a v a . i o . * ;
import j a v a x . s e r v l e t . * ;
/** Дескриптор, который действует как i f / t h e n / e l s e .
p u b l i c c l a s s IfTag extends TagSupport {
p r i v a t e boolean conditions-
p r i v a t e boolean hasCondition = falser-
p u b l i c void s e t C o n d i t i o n ( b o o l e a n c o n d i t i o n ) {
this.condition = condition;
hasCondition = t r u e ;

p u b l i c boolean g e t C o n d i t i o n ( ) {
return(condition);

p u b l i c void setHasCondition(boolean flag) {


this.hasCondition = flag;
}

f-k-k Установлено ли поле условия? */


p u b l i c boolean hasCondition() {
return(hasCondition);

public int doStartTag0 {


return(EVAL_BODY_INCLUDE);
}
}
20.7. Определение новых JSP-дескрипторов 973

Листинг 20.46. IfConditionTag. Java

package c w p . t a g s ;
import javax.servlet.jsp.*;
import javax.servlet.j sp.tagext.*;
import java.io.*;
import javax.servlet.*;

/** Раздел дескриптора, соответствующий условию. */

public class IfConditionTag extends BodyTagSupport {


public int doStartTagO throws JspTagException {
IfTag parent =
(IfTag)findAncestorWithClass(this, IfTag.class);
if (parent == null) {
throw new JspTagException("condition not inside if")/
}
return (EVAL__BODY_TAG) ;
}
public int doAfterBody() {
IfTag parent =
(IfTag)findAncestorWithClass(this, IfTag.class);
String bodyString = getBodyContent().getString();
if (bodyString.trim 0 .equals("true")) {
parent.setCondition(true);
} else {
parent.setCondition(false);
}
return (SKIP__BODY) ;
}
}

Листинг 20.47. IfThenTag. java

package c w p . t a g s ;
import javax.servlet.jsp.*;
import j avax.servlet.j sp.tagext.*;
import java.io.*;
import javax.servlet.*;

/** Раздел then дескриптора. */

public class IfThenTag extends BodyTagSupport {


public int doStartTagO throws JspTagException {
IfTag parent =
(IfTag)findAncestorWithClass(this, IfTag.class);
if (parent == null) {
throw new JspTagException("then not inside if");
} else if (!parent.hasCondition0) {
String warning =
"condition tag must come before then tag";
974 Глава 20. JavaServer Pages

throw new JspTagException(warning);


}
return(EVAL_BODY_TAG);
}
public int doAfterBody() {
IfTag parent =
(IfTag)findAncestorWithClass(this, IfTag.class);
if (parent.getCondition()) {
try {
BodyContent body = getBodyContent();
JspWriter out = body.getEnclosingWriter();
out.print(body.getString());
} catch(lOException ioe) {
System.out.println("Error in IfThenTag: " + ioe)
}
}
return(SKIP_BODY);
}

Листинг 2 0 . 4 8 . I f E l s e T a g . j a v a

package c w p . t a g s ;
import javax.servlet.jsp.*;
import javax.servlet.j sp.tagext.*;
import java.io.*;
import javax.servlet.*;

/** Раздел else дескриптора. */

public class IfElseTag extends BodyTagSupport {


public int doStartTag0 throws JspTagException {
IfTag parent =
(IfTag)findAncestorWithClass(this, IfTag.class);
if (parent == null) {
throw new JspTagException("else not inside if");
} else if (!parent.hasCondition0 ) {
String warning =
"condition tag must come before else tag";
throw new JspTagException(warning);
}
return(EVAL_BODY_TAG);
}
public int doAfterBody() {
IfTag parent =
(IfTag)findAncestorWithClass(this, IfTag.class);
if (?parent.getCondition0) {
try {
BodyContent body = getBodyContent();
JspWriter out = body.getEnclosingWriter0;
20.7. Определение новых JSP-дескрипторов 975

out.print(body.getString());
} catch(lOException ioe) {
System.out.println("Error in IfElseTag: " + ioe);
}
}
return(SKIP_BODY);
}
}

Вложенные дескрипторы: создание файла описания


библиотеки дескрипторов
Несмотря на то что дескрипторы должны занимать определенные места в иерар­
хической структуре, в файле описания библиотеки дескрипторов они определяются
независимо друг от друга (листинг 20.49). Это означает, что проверка вложенности
производится не на этапе преобразования в сервлет, а только при получении запроса.
При необходимости вы можете указать системе на необходимость проверки при кон­
вертировании JSP в сервлет, используя для этого класс T a g E x t r a l n f o . Этот класс
включает метод g e t V a r i a b l e I n f о, который вы можете применять для проверки на­
личия атрибутов и особенностей их использования. Определив подкласс класса
T a g E x t r a l n f o , надо связать его с дескриптором в файле описания библиотеки. Для
этого используется элемент t e i c l a s s , который используется так же, как и t a g c l a s s .
На практике пользоваться T a g E x t r a l n f o несколько неудобно.

Листинг 20.49. c w p - t a g l i b . t l d (фрагмент)

<tag>
<name>if</name>
<tagclass>cwp.tags.IfTag</tagclass>
<info>if/condition/then/else tag.</info>
</tag>
<tag>
<name>condition</name>
<tagclass>cwp.tags.IfConditionTag</tagclass>
<info>condition part of if/condition/then/else tag.</info>
</tag>
<tag>
<name>then</name>
<tagclass>cwp.tags.IfThenTag</tagclass>
<info>then part of if/condition/then/else tag.</info>
</tag>
<tag>
<name>else</name>
<tagclass>cwp.tags.IfElseTag</tagclass>
<info>else part of if/condition/then/else tag.</info>
</tag>
976 Глава 20. JavaServer Pages

Вложенные дескрипторы: создание JSP-файла


в листинге 20.50 показан документ, использующий дескриптор cwp: i f тремя раз­
личными способами. В первом случае вместо выражения непосредственно включено
значение t r u e . Во втором случае в качестве условного выражение использован пара­
метр HTTP-запроса. В третьем случае генерируется случайное число и сравнивается с
пороговым значением. Результаты обращения к док)т^1енту показаны на рис. 20.22.

Листинг 2 0 . 5 0 . I f E x a m p l e . j s p

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>If Tag Example</TITLE>
<LINK REL=STYLESHEET
HREF="JSP-Styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<Hl>If Tag E x a m p l e < / H l >
<%(? t a g l i b u r i = " c w p - t a g l i b . t l d " p r e f i x = " c w p " %>
<cwp:if>
<Gwp:condition>true</cwp:condition>
<cwp:then>Condition i s true</cwp:then>
<cwp:else>Condition is false</cwp:else>
</cwp:if>
<P>
<cwp:if>
<cwp:condition><%= r e q u e s t . i s S e c u r e ( ) %></cwp:condition>
< c w p : t h e n > R e q u e s t i s u s i n g SSL ( h t t p s ) < / c w p : t h e n >
<cwp:else>Request i s not using SSL</cwp:else>
</cwp:if>
<P>
Some c o i n t o s s e s : < B R >
<cwp:repeat reps="10">
<cwp:if>
<cwp:condition><%= Math.random() < 0.5 %></cwp:condition>
<cwp:then><B>Heads</B><BR></cwp:then>
<cwp:else><B>Tails</B><BR></cwp:else>
</cwp:if>
</cwp:repeat>
</BODY>
</HTML>
2 0 . 8 . Интеграция сервлетов и JSP 977

W'^- It Tag Fxample • Ne4tcane "SSBI


ffe £«* ^ew So CommuMcatof b^

1"
^' •>'" 3 г'Л ^
-^.1^" Bookrwwks
^ J-
J^ Location |ht;p//locaihc:. , ^ _,_ _ ^ • • 3
я
If Tag Example
j Condition is true

j Request is not using SSL

1 Some coin tosses:


Heads
Tails
Heads
Tails
Tails
Heads
Tails
Heads
Heads
Heads
Рис. 20.22. Результаты обращения
ijtf^'-^- ''TT^^iSScuBwH Done ... ^М.,:Ш„ .-^r.::i:4 к IfExample.jsp

20.8. Интеграция сервлетов и JSP


Сервлеты — очень удобный инструмент для тех приложений, в которых необходи­
мо выполнять большой объем вычислений. Как было показано в предыдущей главе,
сервлеты мог^о- обрабатывать HTTP-запрос, в частности поля заголовка, использовать
cookie, поддерживать сеанс взаимодействия с клиентом, сохранять информацию в
интервале между запросами, выполнять сжатие документов, обращаться к базам дан­
ных, генерировать GIF-изображения, а также эффективно решать многие другие за­
дачи. Однако генерация сервлетами HTML-кода— это рутинная задача, требующая
больших усилий. Кроме того, сопровождение таких сервлетов затруднено. Здесь на
помощь приходят JSP, они позволяют отделить статический текст документа от ди­
намически генерируемых фрагментов. При работе над JSP Web-дизайнеры могут соз­
давать обычный HTML-текст, используя для этого специальные инструменты, пре­
доставив программистам создавать специфические JSP-фрагменты. JSP-выражения,
скриптлеты и декларации позволяют включать в состав документа Java-код, который
непосредственно переносится в сервлет, генерируемый на основе JSP. Директивы
дают возможность осуществлять общий контроль над структурой документа. При соз­
дании сложных документов вы можете использовать компоненты bean или опреде­
лять новые JSP-дескрипторы.
В связи с вышесказанным может возникн)'ть вопрос: "Является ли JSP универсаль­
ным инструментом, пригодным для решения любой задачи?" Документу JSP соответ­
ствует единственное представление, в котором изменяются лишь детали. Однако мо­
жет возникн)а'ь необходимость генерации различных результатов в зависимости от
принятых данных. Как быть в этом случае? JavaBeans и пользовательские дескрипто­
ры -- мощные инструменты, но они не позволяют преодолеть основное ограничение,
связанное с постоянством общего представления. Решением этой проблемы является
совместное применение сервлетов и JavaSei-ver Pages. В приложении сложной струк­
туры сервлет может выполнять предварительную обработку запроса, устанавливать
978 Глава 2 0 . JavaServer P a g e s

компоненты bean, a затем перенаправлять запрос одному из документов JSP. Данный


подход известен под названием "архитектура модель-просмотр-контроллер" (model
view controller architecture), или model 2. Код, поддерживающий данный подход, вы
найдете по адресу h t t p : / / J a k a r t a , apache . o r g / s t r u t s / .

Перенаправление запросов
Для перенаправления запросов или включения внешнего содержимого используется
класс RequestDispatcher. Для получения объекта RequestDispatcher используется
метод g e t R e q u e s t D i s p a t c h e r класса S e r v l e t C o n t e x t , которому передается URL отно­
сительно корневого каталога сервера. Например, для получения RequestDispatcher,
связанного с h t t p : / / y o u r h o s t / p r e s e n t a t i o n s / p r e s e n t a t i o n l . j s p , надо использо­
вать следующие выражения:
String url = "/presentations/presentationl.jsp";
RequestDispatcher dispatcher =
getServletContext () .getRequestDispatcher(url);
Получив R e q u e s t D i s p a t c h e r , вы можете использовать метод forward для пере­
направления запроса по указанному URL либо метод i n c l u d e для вывода содержимо­
го указанного документа. В обоих случаях методу необходимо передавать в качестве
параметров объекты H t t p S e r v l e t R e q u e s t и H t t p S e r v l e t R e s p o n s e . Оба метода
могут генерировать исключения S e r v l e t E x c e p t i o n и lOException. Например, в
листинге 20.51 показан фрагмент сервлета, перенаправляющего запрос одному из
трех различных JSP-документов. Решение о перенаправлении принимается в зависи­
мости от значения параметра o p e r a t i o n . Для упрощения кода в сервлете использу­
ется метод gotoPage, которому передаются URL, а также объекты H t t p S e r v l e t ­
Request и H t t p S e r v l e t R e s p o n s e . Этот метод посредством вызова g e t R e q u e s t ­
D i s p a t c h e r получает объект R e q u e s t D i s p a t c h e r и вызывает метод forward этого
объекта.

Листинг 2 0 . 5 1 . Пример перенаправления запроса

public void doGet(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, lOException {
String operation = request.getParameter("operation")
if (operation == null) {
operation = "unknown";
}
if (operation.equals("operationl")) {
gotoPage("/operations/presentationl.jsp",
request, response);
} else if (operation.equals("operation2")) {
gotoPage("/operations/presentation2.jsp",
request, response);
} else {
gotoPage("/operations/unknownRequestHandler.j sp",
request, response);
}
20.8. Интеграция сервлетов и JSP 979

private void gotoPage(String address,


HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
RequestDispatcher dispatcher =
getServletContext().getRequestDispatcher(address);
dispatcher.forward(request, response);

Использование статических ресурсов


в большинстве случаев запросы перенаправляются JSP либо другому сервлету. Од­
нако может возникнуть необходимость перенаправления запроса статическому
HTML-документу. Например, на узле электронной коммерции при отсутствии учет­
ной записи пользователя запрос перенаправляется Web-странице, содержащей
HTML-форму. П р и перенаправлении запроса GET статической HTML-странице не
возникает никаких проблем; для этого даже не требуется использование специально­
го синтаксиса. Достаточно указать HTML-документ в качестве параметра g e t ­
R e q u e s t D i s p a t c h e r . Однако при перенаправлении метод запроса остается неиз­
менным, поэтому может возникнуть необходимость обращения к HTML-документу
методом POST. Ч т о б ы решить эту проблему, надо переименовать HTML-файл так,
чтобы его расширение было . j s p . Документ s o m e f i l e . h t m l , переименованный в
somef i l e . j s p , будет одинаково обрабатывать как запрос GET, так и POST.

Передача информации целевому документу


Данные, необходимые JSP-документу, сервлет может хранить в объектах H t t p ­
S e r v l e t R e q u e s t , H t t p S e s s i o n и S e r v l e t C o n t e x t . Эти объекты соответствуют
трем значениям атрибута s c o p e действия j s p : u s e B e a n : r e q u e s t , s e s s i o n и a p p l i ­
cation.
L Информация хранится только в текущем запросе. В этом случае сервлет соз­
дает и сохраняет данные следующим образом:
S o m e C l a s s v a l u e = new SomeClass{...);
request.setAttribute("key", value);
Затем сервлет перенаправляет запрос JSP-документу, который использует для
получения данных следующее действие:
<jsp:useBean id="k:ey" class="SomeClass"
scope="request" />

2. Информация хранится в текущем запросе и в последующих запросах того


лее клиента. Сервлет создает и сохраняет данные следующим образом:
SomeClass value = new SomeClass(...);
HttpSession session = request.getSession(true);
session.setAttribute("key", value);
980 Глава 20. JavaServer Pages

Сервлет перенаправляет запрос JSP-документу, который извлекает данные так,


как показано ниже.
<jsp:useBean id="key" class="SomeClass"
scope="session" />
3. Информация хранится в текущем запросе и в последующих запросах, полу­
ченных от любого клиента. Для создания и сохранения данных сервлет ис­
пользует следующий код:
SomeClass value = new SomeClass (...);
getServletContext().setAttribute("key", value);
Сервлет перенаправляет запрос JSP-дoкyмeнт)^ который извлекает данные так,
как показано ниже.
<jsp:useBean id="key" class="SomeClass"
scope="application" />

Интерпретация относительных URL целевым ресурсом


Несмотря на то что сервлет может перенаправить запрос любому док)^менту на
сервере, существует ряд различий между методами forward и s e n d R e d i r e c t объекта
H t t p S e r v l e t R e s p o n s e . Во-первых, s e n d R e d i r e c t требует установления соедине­
ния клиента с новым ресурсом, в то время как метод forward объекта R e q u e s t -
D i s p a t c h e r полностью поддерживается сервером. Во-вторых, s e n d R e d i r e c t не со­
храняет автоматически данные запроса, что делает forward. В-третьих, использова­
ние s e n d R e d i r e c t приводит к тому, что клиент продолжает взаимодействие с новым
URL, в то время как forward сохраняет URL исходного сервлета.
Последнее означает, что если целевая страница использует относительные URL
для ссылки на изображения или листы стилей, их надо преобразовать так, чтобы они
указывали на ресурс не относительно расположения целевого документа, а относи­
тельно корневого каталога документов. Предположим, например, что в составе доку­
мента содержится следующий дескриптор:
<LINK REL=STYLESHEET
HREF="my-styles.ess"
TYPE="text/css">
Если JSP, содержащий этот код, получил перенаправленный запрос, ссылка н а т у -
s t y l e s .CSS будет интерпретироваться не относительно адреса документа, а относи­
тельно URL сервлета, который перенаправил этот запрос. Это почти наверняка при­
ведет к возникновению ошибки. Для устранения данной проблемы надо переписать
ссылку так, чтобы она содержала полный п)^ь в рамках сервера.
<LINK REL=STYLESHEET
HREF="/path/my-styles.css"
TYPE="text/css">
To же самое надо сделать для дескрипторов <IMG SRC=. . . > и <А HREF=. . . >.
2 0 . 8 . Интеграция сервлетов и JSP 981

Альтернативные способы получения RequestDispatcher


Если сервер поддерживает спецификацию Servlet 2.2, разработчик, помимо вызова
метода g e t R e q u e s t D i s p a t c h e r класса S e r v l e t C o n t e x t , может воспользоваться
двумя дополнительными способами получения объекта R e q u e s t D i s p a t c h e r .
Во-первых, поскольку большинство серверов позволяет регистрировать сервлеты и
JSP, связывая их с именами, для доступа к этим ресурсам можно вместо пути указывать
имя. Для этого используется метод g e t N a m e d D i s p a t c h e r класса S e r v l e t C o n t e x t .
Во-вторых, вы можете указывать при обращении к ресурсу путь относительно рас­
положения текущего сервлета, вместо того, чтобы задавать путь относительно корне­
вого каталога документов. Так как п)п:'ь к JSP нельзя выразить относительно
h t t p : / / h o s t / s e r v l e t / . . ., данный способ используется для доступа к сервлетам.
Однако сервлет можно зарегистрировать, присвоив ему другой путь; в этом случае вы
можете использовать метод g e t R e q u e s t D i s p a t c h e r класса H t t p S e r v l e t R e q u e s t
вместо одноименного метода класса S e r v l e t C o n t e x t . Например, если исходный
сервлет зарегистрирован как h t t p : / / h o s t / t r a v e l / T o p L e v e l , вы можете заменить
строку кода
getServletContext().getRequestDispatcher("/travel/cruises.jsp")
следующим выражением:
request.getRequestDispatcher("cruises.j sp");

Пример: интерактивный агент по обслуживанию


туристов
Рассмотрим реализацию интерактивного агента по обслуживанию туристов, кото­
рый поддерживает Web-страницу с возможностями поиска (рис. 20.23 и листинг
20.52). Для того чтобы связать запрос с имеющейся учетной записью, пользователь
должен ввести свой почтовый адрес и пароль. В каждом запросе также указываются
исходный пункт, пункт назначения, даты выезда и окончания путешествия. Результа­
ты запроса существенно зависят от действий пользователя. Например, если пользо­
ватель щелкнет мышью на кнопке Воок Flights, он получит список рейсов, соответст­
вующих указанным датам и упорядоченных по цене (рис. 20.24). Для генерации такой
страницы необходима инфорхмация о рейсах и номер платежной карточки пользова­
теля. После щелчка на кнопке Edit Account будет отображена информация о пользо­
вателе с возможностью изменения и добавления данных. Активизация кнопок Rent
Cars и Find Hotels приведет к выводу совершенно других Web-страниц.
Для того чтобы реализовать требуемое поведение системы, страница, используе­
мая в качестве первичного интерфейса (листинг 20.52), передает запрос сервлету
верхнего ) р о в н я , код которого показан в листинге 20.53. Данный сервлет ищет ин­
формацию о потребителе (реальный код расположен по адресу h t t p : / / w w w .
c o r e w e b p r o g r a m m i n g . c o m , на практике поиск выполняется в базе данных), переда­
ет ее объекту H t t p S e s s i o n , связывая значение типа c w p . T r a v e l C u s t o m e r с именем
c u s t o m e r , а затем перенаправляет запрос JSP-документу, соответствующему конкрет­
ному действию пользователя. Целевой документ (листинг 20.54 и рис. 20.24) ищет
данные о потребителе посредством след)^ющего кода:
982 Глава 2 0 . JavaServer Pages

<jsp:useBean id="customer"
class="cwp.TravelCustomer"
scope="session" />
a затем использует действие j s p i g e t P r o p e r t y для включения и н ф о р м а ц и и в раз­
личные части результирующей страницы.
Особое внимание следует уделить классу T r a v e l C u s t o m e r (часть его показана в
листинге 20.55, а полностью его код хранится по адресу www. c o r e w e b p r o g -
r a m m i n g . com). В частности, заметьте, что значительная часть кода посвящена тому,
чтобы сделать информацию о потребителе доступной посредством свойств в виде
строк и даже форматированного HTML-текста. Каждая задача, требующая больших
усилий по созданию программ, решается путем создания компонентов bean. Такой
подход типичен для интеграции сервлетов и JSP — использование JSP не исключает
полностью необходимости форматировать данные посредством Java-кода, представ­
ляя их в виде строк и HTML-элементов. Усилия по форматированию информации,
чтобы она была доступна JSP, многократно окупают себя в случае, если к одним и тем
же данным обращается несколько документов.

1^Л|Д1Ш1Ит1>|1!М'1:Ш|||.|у'11У1Я4.1|11^1Ш|^|1|11^^^^^^

У^;^ ШШШ^
i |АЙ#ЙИ \Ш1 Ntp://loe«lM»tArevei/ouick-t8erchh(ml

~

Online Travel Qiil


Email address: |joe@somehost.com
Pagyword: F^*^
O n y h r JBaltimore

D e s t i l i a t i o u ; |LOS Angeles ^
Start date (MMDDA^'): |1Л/2001
Eiid date (MM''DDA'\') |i/8/200i

ggokBghl 1 йетОу j flnciHoiei | Ecl<tAccoa«t

Not vet a member? Get a free account here.


J
;^"Pe^"

Рис. 20.23. Интерфейс интерактивного агента (листинг 20.52)


20.8. Интеграция сервлетов и JSP 983

Листинг20.52. /travel/quick-search.html (фрагмент)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Online Travel Quick Search</TITLE>
<LINK REL=STYLESHEET
HREF="travel-styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<BR>
<Hl>Online Travel Quick Search</Hl>
<FORM ACTION="/servlet/cwp.Travel" METHOD="POST">
<CENTER>
Email address: <INPUT TYPE="TEXT" NAME="emailAddress"><BR>
Password: <INPUT TYPE="PASSWORD" NAME="password" SIZE=10><BR>

<TABLE CELLSPACING=1>
<TR>
<TH>&nbsp;<IMG SRC="airplane.gif" WIDTH=100 HEIGHT=29
ALIGN="TOP" ALT="Book Flight">&nbsp;

<TR>
<TH><SMALL>
<INPUT TYPE="SUBMIT" NAME="flights" VALUE="Book Flight">
</SMALL>

</TABLE>
</CENTER>
</FORM>

</BODY>
</HTML>
984 Глава 20. JavaServer Pages

i ^ Best Av«iable Flight» ^MicfOMifl Internet Exploiei ~ """ ~""^ " ~ ' ~ ' 'шШШ
£te £(* y«w Favodtej Jocfe Нф

^ - • - J ^ :3' a -iJ -^ ^J)- -J


http //tocalhosi/'servlet/'cwp Travel
^3
j
1 B e s t Available Flights
Fiiiduiafliglit.s'for Joe Hacka

^ П ^ ^ ^ Ж : ^ ^ ^ : ^ ? ' ^ ' " ? ' ^ V ; Java AlrwarsFliglit 1522 ($455.95) ^ '^^<:^-'^-; ; • • ' •

Oufgomg: Lea\ es> Baltimore at 9 00 AM on 1/1/2001, aniMiig in Los Angelefc; at 3 15 PM (1 stop - Java, Indonesia).
Retiuii: Lea\es Los Angeles at 900 AM on 1/8/2001, arming in Baltimore at Ъ15 PM (1 stop -- Sim Microsystems),

\ . SfnletEa^ireirsijri^tadaa (1^505.95)

Outgoing: Leaves Baltimore at 9,30 AM on 1 1/2001, aiTi\ing in Los Angeles at 415 PM (1 stop -- Ne>^' Atlanta)
Retmii: Leaves Los Angeles at 9 30 AM on 1/8/2001, anivuig in Baltimore at 415 PM (1 stop -- New Atlanta)

^^^^ '^-^V^;ki.~7''^ >?


ОеекАШШе«ГЙ?||<:ЗЛ415^<$<?75.О0) ~ ^''г ^ ^ ^^ ^^^ ^-^^^^^-V'\;:\^ ^'^

Outgoing: L€a\es Balhmore at 10.02;3'7 . ^ I on 1/1/2001, aiTi\ing ui Los Angeles at 2 22 19 PM (1 stop -- JHIT)
Return: Leaves Los Angeles at 10:0237 AM on l/8/'2001, aiTi\iiig ш Balhmore at 2 22 19 PM (1 stop -- МГГ)

1 Aiiiiue Fre«|uent Flyer Number


Ja\Ti Airways 321-9299-J
United 442-2212-U
Soiitliwest 1A345

Crf dir Cai'd: JavaSmartCard (XXXX-XXXX-XXXX-3120)

1 HoWfor24Hrs j

Book It! 1
J
i ^ Don* 1 ^ \joceA irtranei

Рис. 20.24. Сервлет, реализующий интерактивный агент (листинг 20.53), перенаправляет запрос
документу B o o k F l i g h t s . j sp (листинг 20.54)
20.8. Интеграция сервлетов и JSP 985

Листинг 2 0 . 5 3 . T r a v e l . j a v a

package cwp;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Сервлет верхнего уровня. Этот сервлет оформляет данные


* о потребителе как bean, затем перенаправляет запрос
* требуемому документу.

public class Travel extends HttpServlet {


private TravelCustomer[] travelData;

public void initO {


travelData = TravelData.getTravelData();
}
/** Поскольку в составе запроса передается пароль,
используется запрос POST. */

public void doPost(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, lOException {
String emailAddress = request.getParameter("emailAddress");
String password = request.getParameter("password");
TravelCustomer customer =
TravelCustomer.findCustomer(emailAddress, travelData);
if ((customer == null) I I (password == null) I I
([password.equals(customer.getPassword()))) {
gotoPage("/travel/accounts.jsp", request, response);
}
// Методы, которые используют следующие параметры,
// проверяют, корректно ли введены данные,
customer.setStartDate(request.getParameter("startDate"));
customer.setEndDate(request.getParameter("endDate"));
customer.setOrigin(request.getParameter("origin"));
customer.setDestination(request.getParameter
("destination"));
HttpSession session = request.getSession(true);
session.setAttribute("customer", customer);
if (request.getParameter("flights") != null) {
gotoPage("/travel/BookFlights.jsp",
request, response);
} else if (request.getParameter("cars") != null) {
gotoPage("/travel/RentCars.j sp",
request, response);
} else if (request.getParameter("hotels") != null) {
gotoPage("/travel/FindHotels.jsp",
request, response);
} else if (request.getParameter("cars") != null) {
gotoPage("/travel/EditAccounts.jsp".
986 Глава 2 0 . JavaServer Pages

request, response);
} else {
gotoPage("/travel/IllegalRequest.j sp",
request, response);
}
}

private void gotoPage(String address,


HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
RequestDispatCher dispatcher =
getServletContext().getRequestDispatcher(address)
dispatcher.forward(request, response);
}

Листинг 20.54. BookFlights.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Best Available Flights</TITLE>
<LINK REL=STYLESHEET
HREF="/travel/travel-styles.ess"
TYPE="text/css">
</HEAD>
<BODY>
<Hl>Best Available Flights</Hl>
<CENTER>
<jsp:useBean id="customer"
class="cwp.TravelCustomer"
scope="session" />
Finding flights for
<jsp:getProperty name="customer" property="fullName" />
<P>
<jsp:getProperty name="customer" property^"flights" />
<P><BR><HR><BR>
<FORM ACTION="/servlet/BookFlight">
<jsp:getProperty ПсШ1е="customer"
property="frequentFlyerTable" />
<P>
<B>Credit Card:</B>
<jsp:getProperty name="customer" property="creditCard" />
<P>
<INPUT TYPE="SUBMIT" NAME="holdButton" VALUE="Hold for 24 Hrs">
<P>
<INPUT TYPE="SUBMIT" NAME="bookItButton" VALUE="Book It!">
</FORM>
</CENTER>
</BODY>
</HTML>
2 0 . 8 . Интеграция сервлетов и JSP 987

Листинг 20.55. TravelCustomer. Java (фрагмент)

package cwp;
import java.util.*;
import Java.text.*;

/** Данный класс описывает потребителя. Он реализован


* как bean; некоторые методы возвращают данные в
* HTML-формате.

public class TravelCustomer {


private String emailAddress, password, firstName, lastName;
private String creditCardName, creditCardNumber;
private String phoneNumber, homeAddress;
private String startDate, endDate;
private String origin, destination;
private FrequentFlyerlnfo[] frequentFlyerData;
private RentalCarInfо[] rentalCarData;
private HotelInfo[] hotelData;

public TravelCustomer(String emailAddress,


String password.
String firstName,
String lastName,
String creditCardName,
String creditCardNumber,
String phoneNumber,
String homeAddress,
FrequentFlyerlnfo[] frequentFlyerData,
RentalCarlnfo[] rentalCarData,
HotelInfo[] hotelData) {
setEmailAddress(emailAddress);
setPassword(password);
setFirstName(firstName);
setLastName(lastName);
setCreditCardName(creditCardName);
setCreditCardNumber(creditCardNumber);
setPhoneNumber(phoneNumber);
setHomeAddress(homeAddress);
setStartDate(startDate);
setEndDate(endDate);
setFrequentFlyerData(frequentFlyerData);
setRentalCarData(rentalCarData);
setHotelData(hotelData);
}

public String getEmailAddress() {


return(emailAddress);
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
988 Глава 2 0 . JavaServer Pages

public String getCreditCard() {


String cardName = getCreditCardName();
String cardNum = getCreditCardNumber();
cardNum = cardNum.substring(cardNum.length() - 4 ) ;
return(cardName + " (XXXX-XXXX-XXXX-" + cardNum + " ) " ) ;
}
private String getFlightDescription(String airline.
String flightNum,
String price,
String stopl,
String stop2,
String timel,
String time2,
String flightOrigin,
String flightDestination,
String flightStartDate,
String flightEndDate) {
String flight =
"<P><BR>\n" +
"<TABLE WIDTH=\"100%\"><TR><TH CLASS=\"COLORED\">\n" +
"<B>" + airline + " Flight " + flightNum +
" ($" + price + ")</B></TABLE><BR>\n" +
•"<B>Outgoing:</B> Leaves " + flightOrigin +
" at " + timel + " AM on " + flightStartDate +
", arriving in " + flightDestination +
" at " + time2 + " PM (1 stop — " + stopl + ").\n" +
"<BR>\n" +
"<B>Return:</B> Leaves " + flightDestination +
" at " + timel + " AM on " + flightEndDate +
", arriving in " + flightOrigin +
" at " + time2 + " PM (1 stop -- " + stop2 + ").\n";
return(flight);
}

Перенаправление запросов JSP-документами


Чаще всего перенаправление запросов выполняется по следующему сценарию:
сервлет получает запрос и перенаправляет его JSP-документу. Так происходит потому,
что для первичной обработки запроса, проверки параметров и установки bean обыч­
но требуется большой объем программного кода; этот код удобнее реализовать в
сервлете, чем в JSP. В роли целевого документа обычно выступает JSP, поскольку дан­
ная технология существенно упрощает генерацию HTML-содержимого.
Тот факт, что описанный подход применяется чаще всего, совсем не означает, что
он должен использоваться всегда. Для генерации результирующего документа может
применяться сервлет. Также вполне вероятно, что перенаправлением запроса друго­
му ресурсу станет заниматься JSP. Так, например, JSP может обрабатывать запрос, ге­
нерировать результаты, а в случае, если в составе запроса содержатся данные, кото­
рые он не поддерживает, — перенаправить запрос сервлету.
20.8. Интеграция сервлетов и JSP 989

Передача запроса сервлету вместо JSP не требует изменений в действиях, выпол­


няемых с R e q u e s t D i s p a t c h e r . Однако при перенаправлении запроса JSP-докумен-
том вместо включения R e q u e s t D i s p a t c h e r в скриптлет желательно использовать
более простое действие j s p : f o r w a r d . Это действие задается в следующем формате:
<jsp:forward p a g e = " R e l a t i v e URL" / >
Атрибут p a g e позволяет задавать JSP-выражение, поэтому значение данного атри­
бута может вычисляться в момент получения запроса. Например, след)^ющий фраг­
мент кода перенаправляет приблизительно половину посетителей по адресу
h t t p : / / h o s t / e x a m p l e s / p a g e l . j s p , в то время как другая половина работает с до­
кументом h t t p : / / h o s t / e x a m p l e s / p a g e 2 . j s p .
<% S t r i n g d e s t i n a t i o n ;
i f (Math.random0 > 0.5) {
destination = "/examples/pagel.jsp";
} else {
destination = "/examples/page2.jsp";
}
%>

20.9. Резюме
в принципе, любая задача, решаемая посредством JSP, может быть решена с по­
мощью сервлетов. Однако на практике JSP упрощают процед)ру генерации HTML-
содержимого и обеспечивают более высокую надежность. Это достигается за счет
разделения статического текста, используемого для оформления Web-страницы, и
Java-кода, генерирующего динамические фрагменты документа. Работу по созданию
различных частей такого документа легко распределить между разными группами
разработчиков.
В результате лишь на немногих узлах используются только сервлеты и не приме­
няются JSP. Однако это не означает, что разработчики программ, предназначенных
для выполнения на стороне сервера, полностью отказались от применения сервлетов
в пользуJSP. Как правило, для одних задач используются сервлеты, для других — JSP, а
для третьих — сочетание сервлетов и JSP.
В данной главе мы рассказали вам об основных средствах JSP и показали, как про­
исходит управление сервлетами, генерируемыми на основе JSP. Вы также узнали о
применении компонентов bean и пользовательских дескрипторов. Эти возможности
позволяют поручить сопровождение сложных документов сотрудникам, не имеющим
опыта в программировании на языке Java. И, наконец, мы рассмотрели способы объе­
динения сервлетов и JSP для решения сложных задач.
Итак, отдохните немного и приготовьтесь отвечать на многочисленные звонки
работодателей, нуждающихся в услугах такого квалифицированного специалиста, ка­
ким стали вы.
АПЛЕТЫ
КАК ИНТЕРФЕЙС
К ПРОГРАММАМ
НА СТОРОНЕ
СЕРВЕРА

В ЭТОЙ главе...

• Передача запроса GET И обработка результатов с помощью


броузера.

• Передача запроса GET И обработка результатов с помощью


аплета (НТТР-туннелирование).

• Использование средств сериализации для передачи


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

• Передача запроса POST И обработка результатов с


помощью аплета (НТТР-туннелирование).

• Сетевое взаимодействие без использования


HTTP-сервера.
Tly\^sJZJ

TML-формы, которые обсуждались в главе 18, обеспечивают простой способ

H получения данных от пользователя и передачи их сервлету либо CGI-


программе. Однако возможности HTML-форм ограничены. В некоторых слу­
чаях для выполнения поставленной задачи требуется более сложный интерфейс. Ап-
леты позволяют управлять размерами, задавать цвет и н т е р ф е й с н ы х элементов и оп­
ределять ш р и ф т для отображения. Разработчик аплетов может использовать различ­
ные компоненты, например кнопки, л и н е й н ы е регуляторы, раскрывающиеся окна. В
аплете можно выполнять рисование, обработку событий мыши и клавиатуры, органи­
зовывать специальные ф о р м ы ввода; кроме того, данные, введенные пользователем,
можно передавать различным программам на стороне сервера. Н о чтобы реализовать
эти возможности, необходимо затратить время и усилия на создание соответствую­
щего программного кода, в то время как HTML-формы создаются просто и быстро.
Поэтому решение о том, должен ли и н т е р ф е й с быть реализован с помощью HTML-
форм или посредством аплета, принимается исходя из условий конкретной задачи.
Структура HTML-формы практически не зависит от типа запроса. Конкретный
метод запроса задается посредством атрибута METHOD элемента FORM. П р и работе с
аплетами для ф о р м и р о в а н и я запроса могут быть использованы т р и различных подхо­
да. Согласно первому подходу, который рассматривается в разделе 21.1, аплет фор­
мирует запрос GET, передаваемый серверу, а полученные в ответ данные обрабаты­
ваются броузером. П р и м е р такого подхода приводится в разделе 21.2. Следуя второму
подходу, рассматриваемому в разделе 21.3, аплет передает запрос GET и связанные с
ним данные сервлету, а затем сам обрабатывает результаты. Соответствующий при­
мер приводится в разделе 21.4. Т р е т и й подход, рассматриваемый в разделе 21.5, со­
стоит в том, что аплет передает сервлету запрос POST, а затем сам обрабатывает ре­
зультаты. П р и м е р приводится в разделе 21.6. Наконец, в разделе 21.7 обсуждается не­
посредственное взаимодействие аплета с программой на стороне сервера, причем
HTTP-сервер в процессе обмена не участвует.
Чтобы понять материал, изложенный в данной главе, необходимо иметь общие
представления об аплетах (см. главу 9) и о взаимодействии с программами на стороне
сервера.
992 Глава 2 1 . Аплеты как интерфейс.

2 1 . 1 . Передача данных с использованием


запроса GET и отображение
результатов
Метод showDocument указывает броузеру на то, что тот должен отобразить доку­
мент, на который указывает заданный URL. Чтобы передать данные сервлету или
CGI-программе, их надо присоединить к URL ресурса через знак вопроса ("?"). Если
аплет должен передавать данные, их следует присоединить к URL и передать полу­
ченную строк)' в качестве параметра методу showDocument. В приведенном ниже
примере предполагается, что baseURL содержит строку, представляющую URL про­
граммы на стороне сервера, а some D a t a содержит информацию, которая должна
быть передана этой программе в составе запроса.
try {
URL programURL = new URL(baseURL + "?" + someData);
getAppletContext().showDocument(programURL);
} catch(MalformedURLException mue) { ... }
Если данные передаются броузером, они кодируются, т.е. пробелы заменяются
знаками "+", а каждый символ, отличный от латинских букв и ц и ф р , заменяется по­
следовательностью, состоящей из знака "%" и двух шестнадцатеричных ц и ф р , пред­
ставляющих код символа. В предыдущем примере предполагается, что данные в
s o m e D a t a закодированы корректно, в противном случае при обработке запроса воз­
никнет ошибка. В JDK 1.1 и более поздних версиях этого пакета содержится класс
URLEncoder, в котором определен статический метод e n c o d e , выполняющий необ­
ходимые действия по кодированию. Если аплет взаимодействует с программой на
стороне сервера, обрабатывающей запросы GET, аплет должен кодировать строку па­
раметров, оставляя неизменными лишь знак "=", разделяющий имя и значение пара­
метра, и символ "&", разделяющий пары имя=значение. Таким образом, вы не можете
вызывать URLEncoder. e n c o d e ( s o m e D a t a ) , вместо этого надо отдельно кодировать
значение каждого параметра. В этом случае строка s o m e D a t a будет формироваться и
передаваться программе на стороне сервера приблизительно следующим образом:
String someData =
namel + "=" + URLEncoder.encode(vail) + "&" +
name2 + "=" + URLEncoder.encode(val2) + "&" +

nameN + "=" + URLEncoder.encode(valN);

try {
URL programURL = new URL(baseURL + "?" + someData);
getAppletContext().showDocument(programURL);
} catch(MalformedURLException mue) { ... }
П р и м е р использования данного подхода приведен в следующем разделе.
2 1 . 2 . Интерфейс к нескольким поисковым серверам 993

21.2. Интерфейс к нескольким поисковым


серверам
В листинге 2L1 показан аплет, в котором для получения информации от пользова­
телей применяется поле редактирования. Когда пользователь вводит информацию,
аплет кодирует значение, заданное в текстовом поле, и генерирует три URL для за­
просов GET, связывая с ними данные. Один из запросов адресуется Google, второй —
Infoseek, третий — Lycos. Для передачи запросов используется метод showDocument, а
при получении ответов броузер отобразит результаты в трех различных фреймах. Эта
задача не может быть решена посредством HTML-формы, поскольку форма передает
запрос только по одному URL. В листинге 12.2 представлен класс SearchSpec, ис­
пользуемый аплетом для генерации URL, необходимых для перенаправления запро­
сов различным поисковым серверам. Класс SearchSpec также может использоваться
сервлетами. Результаты выполнения аплета показаны на рис. 2L1 и 2L2.
В листинге 2L3 показан код HTML-страницы верхнего уровня, а в листинге 2L4 —
HTML-дoк)^мeнт, содержащий аплет и отображаемый в одном из фреймов. Если вас
интересует содержимое трех небольших файлов, первоначально отображаемых в
трех фреймах (рис. 2L1), вы можете найти их по адресу h t t p : / / w w w . coreweb-
programming.com/.

Листинг 2 1 . 1 . S e a r c h / ^ p l e t . Java

import Java.applet.Applet;
import java.awt.*;
import Java.awt.event.*;
import java.net.*;

/** Аплет читает значение из объекта TextField, а затем


* использует его для создания трех URL, в состав которых
* включаются данные, соответствующие запросу GET.
* Один из этих URL указывает на Google, второй - на
* Infoseek, а третий - на Lycos. Броузер получает
* данные, соответствующие всем трем URL, и отображает их
* в разных фреймах. Заметьте, что стандартные
* HTML-формы не позволяют одновременно формировать несколько
* запросов.
V
public class SearchApplet extends Applet
implements ActionListener {
private TextField queryField;
private Button submitButton;

public void initO {


setBackground(Color.white);
setFont(new Font("Serif", Font.BOLD, 18));
add(new Label("Search String:"));
queryField = new TextField(40);
queryField.addActionListener(this);
add(queryField);
submitButton = new Button("Send to Search Engines");
994 Глава 2 1 . Аплеты как интерфейс...

submitButton.addActionListener(this);
add(submitButton);
}
/** Данные передаются после щелчка на кнопке <В>или</В>
* после нажатия клавиши <Enter> в тот момент, когда
* фокус ввода принадлежит полю редактирования.
Ч
public void actionPerformed(ActionEvent event) {
String query = URLEncoder.encode(queryField.getText());
SearchSpec[] coinmonSpecs = SearchSpec.getCommonSpecs();
// HotBot (последний пункт) пропускается, поскольку
/ / в данном случае для вывода результатов в верхний
// фрейм используется JavaScript to. Поэтому ниже
// указано значение length-1.
for(int i=0; i<coimnonSpecs.length-1; i++) {
try {
SearchSpec spec = commonSpecs[i];
// Класс SearchSpec создает URL в формате,
// соответствующем популярным поисковым серверам.
URL searchURL = new URL(spec.makeURL(query, "10"));
String frameName = "results" + i;
getAppletContext().showDocument(searchURL, frameName);
} catch(MalformedURLException mue) {}
}
}

Листинг 21.2.SearchSpec.Java

/** Простой класс, инкапсулирующий средства создания


* запросов к поисковым серверам.

public class SearchSpec {


private String name, baseURL, numResultsSuffix;

private static SearchSpec[] commonSpecs =


{ new SearchSpec("google",
"http://www.google.com/search?q=",
"&num="),
new SearchSpec("infoseek",
"http://infoseek.go.com/Titles?qt=",
"&nh="),
new SearchSpec("lycos",
"http://lycospro.lycos.com/cgi-bin/" +
"pursuit?query=",
"&maxhits="),
new SearchSpec("hotbot",
"http://www.hotbot.com/?MT=",
"&DC=")
};
21.2. Интерфейс к нескольким поисковым серверам 995

public SearchSpec(String name,


String baseURL,
String numResultsSuffix) {
this.name = name;
this.baseURL = baseURL;
this.numResultsSuffix = numResultsSuffix;
}
public String makeURL(String searchString,
String numResults) {
return(baseURL + searchString +
numResultsSuffix + numResults);
}
public String getNameO {
return (name) ;
}
public static SearchSpec[] getCommonSpecs() {
return(commonSpecs);
}

i-y{jriiaHmi«iiiiHiiiiffliH.;HHiifflHiHaa

Search Stiiiig:
S«tiil to Scardi Enffaiei [

Google Results Will Go Infoseek Results Will Go Lycos Results Will Go


Here Here Here

Рис. 21.1. Аплет SearchApplet позволяет пользователю передавать один набор ключевых слов
нескольким поисковым серверам
996 Глава 2 1 . Аплеты как интерфейс.

БШШ
lie £d* Yew F^vortes Tooh НФ

Search Striii;: Servlets JarvaServer Pages Book


Send to Seardt Eiifpaies

JServlets Jeve •щштшЕ^ат


Google JAII Language
Google Se
?е?'С1 Tp^
GQc
Search > Seivleis JavaSefvet Pages Book
(*' H«:Vv search <^ Search within results

JServlets JavaServer Pages Book Fmt


b>*»tch opt!or>i ) How to гллгсЬ | GOquai-diar'" is ResuttS for jSetvlets and JavaServer P. ^

^ *i: CQm[.iuterc- > Programtning > Langua


YOU'VE GOT: eCOMMEl
СC-Q Servtets fW,:i JavaServer Pages К.Уf.K HFBEJ
Microsystems Press/Prentice Hall PTR Book Wee SITES >V i^^ : ^ i > ; . ,
ISBN 0-13-089340-4 This new
HJ-IJ.I.IJJ,JIIUJ
1 match»f Hid«
Home page for new servlet/JSP jufTirr^jflt? I gort fry
book from Sun Microsystems Press and
Prentice Hall Covers servlots
I d k i Упа^фцс fWJits •'г^Шз,„
• ТЛГ'- •ig'>E._SjvejjgLiQ.5P,%,fin-.6?s,tsgilirig-B'20
1 Object News GO Shopping
CotT.puters ;> Programming > Ldnijujges > Java ^ Cuiient •Mil ions oforoduct i l
.ft.v ,i,--p.'l'-n> " 'J StlSMJILai£il£i NETWORK !• Serylets.pprri
• 1000's of brands
COMPUTING I Useful L'PLs:
• Search GO Shopp m :...M. ^
iL J ^
TAKES FIVE
[f^J 1

Рис. 21.2. В ответ на запрос отображаются результаты поиска, выполненного тремя серверами

Листинг 2 1 . 3 . P a r a l l e l S e a r c h e s . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 F r a m e s e t / / E N " >


<HTML>
<HEAD>
<TITLE>Parallel Search Engine Results</TITLE>
</HEAD>

<FRAMESET ROWS="120,*">
<FRAME SRC="SearchAppletFrame.html" SCROLLING="NO">
<FRAMESET COLS="*, *f''">
<FRAME SRC="GoogleResultsFrame.html" NAME="resultsO">
<FRAME SRC="InfoseekResultsFranie.html" NAME="resultsl">
<FRAME SRC="LycosResultsFrame.htral" NAME="results2">
</FRAMESET>
</FRAMESET>

Листинг 21.4.SearchAppletFrame.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Search Applet Frame</TITLE>
21.3. Использование запроса GET... 997

</HEAD>

<BODY BGCOLOR="WHITE">
<CENTER>
<APPLET CODE="SearchApplet.class" WIDTH=600 HEIGHT=100>
<B>This example requires a Java-enabled browser.</B>
</APPLET>
</CENTER>
</BODY>
</HTML>

21.3. Использование запроса GET


и непосредственная обработка
результатов (НТТР-туннелирование)
в предыдущем примере аплет указывал броузеру на то, что тот должен отобразить
выходные данные программы на стороне сервера в одном из фреймов. Использова­
ние броузеров для отображения результатов полностью оправдано в тех случаях, ко­
гда вы работаете с существующими службами, поскольку практически все CGI-
программы возвращают HTML-документы. Однако, если вы разрабатываете и клиент-
программу, и программу, работающую на стороне сервера, требование всегда воз­
вращать HTML-документ существенно увеличило бы объем работы по созданию про­
дукта. В некоторых случаях гораздо проще возвращать данные аплету, не оформляя
их в виде специального документа. Данный подход называют НТТР-туннелирова-
нием, поскольку в составе HTTP-пакетов передаются сообщения, соответствующие
другому протоколу. При этом могут использоваться ргоху-серверы, осуществляться
кодирование, перенаправление запросов, поддерживаться взаимодействие через
брандмауэр и т.п.
Существуют две разновидности описанного подхода. В обоих случаях для создания
входного потока, соответствующего соединению с указанным URL, применяется
класс U R L C o n n e c t i o n . Различие состоит в типе потока. В первом случае используется
B u f f e r e d l n p u t S t r e a m или другой низкоуровневый поток, который позволяет чи­
тать двоичные или ASCII-данные, передаваемые программой на стороне сервера. Во
втором случае применяется поток O b j e c t I n p u t S t ream, позволяющий принимать
сложные структуры данных. Это возможно только в том случае, когда программа, вы­
полняющаяся на стороне сервера, также написана на языке Java.

Чтение двоичных и ASCII-данных


Для чтения данных, переданных сервером, аплет должен сначала создать на базе
URL ресурса объект U R L C o n n e c t i o n , а затем связать с ним поток Buf f e r e d l n p u t ­
S t r e a m . Действия, необходимые лля. решения этой задачи, описаны ниже и выпол­
няются в семь этапов. В данном случае мы не рассматриваем код на стороне сервера,
поскольку код клиента позволяет организовать взаимодействие с любой программой
и даже со статическим HTML-дoкvмeнтoм.
998 Глава 2 1 . Аплеты как интерфейс.

При выполнении большинства операций с потоками может генерироваться ис­


ключение lOException, поэтому приведенные ниже выражения должны включаться
в блок t r y / c a t c h .
1. Создание объекта URL, указывающего на узел, с которого был загружен
аплет. Конструктору класса URL можно передать строку, содержащую абсолют­
ный URL, но поскольку броузер позволяет аплету устанавливать соединения
только с тем узлом, с которого он был загружен, то целесообразно создавать
объект URL следующим образом:
URL c u r r e n t P a g e = getCodeBase ( ) ;
String protocol = currentPage.getProtocol();
String host = currentPage.getHost()/
int port = currentPage.getPort();
String urlSuffix = "/servlet/SomeServlet";
URL dataURL = new URL(protocol, h o s t , p o r t , u r l S u f f i x ) ;
2. Создание объекта URLConnection. Метод openConnection класса URL воз­
вращает объект URLConnection, который может быть использован для полу­
чения объекта потока.
URLConnection c o n n e c t i o n = d a t a U R L . o p e n C o n n e c t i o n ( ) /
3. Запрет кэширования данных броузером. Первое, что необходимо сделать,
получив объект URLConnection, — запретить броузеру кэшировать данные.
connection.setUseCaches(false);
4. Установка требуемых полей HTTP-заголовка. Если вы собираетесь устанав­
ливать поля заголовка HTTP-запроса (см. раздел 19.7), то можете сделать это с
помощью метода s e t R e q u e s t P r o p e r t y .
connection.setRequestProperty("header", "value");
5. Создание входного потока. Для ввода данных могут быть использованы раз­
личные типы потоков; чаще других применяется Buf f eredReader. При созда­
нии входного потока устанавливается соединение с Web-сервером.
BufferedReader in =
new BufferedReader(new InputStreamReader(
connection.getInputStream{)));
6. Чтение содерлсимого документа. Согласно спецификации HTTP, по оконча­
нии передачи ответа сервер закрывает соединение. Если соединение закрыто,
метод r e a d L i n e возвращает значение n u l l .
S t r i n g liner-
w h i l e ( ( l i n e = i n . r e a d L i n e ( ) ) != n u l l ) {
doSomethingWith(line);
}
7. Закрытие входного потока.
in.close();
2 1 . 3 . Использование запроса GET... 999

Чтение сериализованных структур данных


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

Действия на стороне клиента


Для чтения сериализованных данных, переданных сервлетом, аплет должен выпол­
нить перечисленные ниже действия. От чтения ASCII-данных отличаются только этапы
5 и 6 процедуры чтения структурированной информации. Код, демонстрирующий вы­
полнение соответствующих действий, упрощен за счет отказа от блоков t r y / c a t c h .
1. Создание объекта URL, указывающего на узел, с которого был загрулсен
аплет. Как и прежде, на данном этапе создается только путь к сервлету, осталь­
ная часть URL определяется автоматически.
URL c u r r e n t P a g e = g e t C o d e B a s e ( ) /
String protocol = currentPage.getProtocol();
String host = currentPage.getHost();
int port = currentPage.getPort0/
String urlSuffix = "/servlet/SomeServlet";
URL dataURL = new URL(protocol, h o s t , p o r t , u r l S u f f i x ) ;
2. Создание объекта URLConnection. Метод openConnection класса URL воз­
вращает объект URLConnection, который используется для получения потока.
URLConnection c o n n e c t i o n = d a t a U R L . o p e n C o n n e c t i o n ( ) ;
3. Запрет кэширования данных броузером. Первое, что необходимо сделать,
получив объект URLConnection, — запретить броузеру кэшировать данные.
connection.setUseCaches(false);
4. Установка требуемых полей HTTP-заголовка. Если вы собираетесь устанав­
ливать поля заголовка HTTP-запроса (см. раздел 19.7), то можете сделать это с
помощью метода s e t R e q u e s t P r o p e r t y .
connection.setRequestProperty("header", "value");
5. Создание потока ObjectlnputStream. Конструктор этого класса получает объ­
ект потока от класса URLConnection. При создании входного потока устанав­
ливается соединение с Web-сервером.
ObjectlnputStream in =
new ObjectInputStream(connection.getInputStream());
1000 Глава 2 1 . Аплеты как интерфейс.

6. Чтение структуры данных с помощью метода readObject. Метод r e a d O b j e c t


возвращает значение Ob j e c t , поэтому необходимо привести полученные данные
к типу объекта, который был реально передан сервером.
SomeClass v a l u e = (SomeClass)in.readObject();
doSomethingWith(value);
7. Закрытие входного потока.
in.close ();

Действия на стороне сервера


Для передачи аплет)^ сериализованных объектов сервлет должен выполнить опи­
санные ниже действия. Здесь предполагается, что переменные r e q u e s t и r e s p o n s e
содержат ссылки на объекты H t t p S e r v l e t R e q u e s t и H t t p S e r v l e t R e s p o n s e , пере­
даваемые методам d o G e t и d o P o s t .
1. Установка поля заголовка, указывающего на то, что в составе ответа пере­
даются двоичные данные. В составе заголовка ответа необходимо задать
М1МЕ-ТИП a p p l i c a t i o n / x - j a v a - s e r i a l i z e d - o b j e c t . Этот MIME-тип явля­
ется стандартным для объектов, передаваемых через поток O b j e c t O u t p u t -
S t r e a m , однако на практике, если данные предназначены не для броузера, а
для аплета, MIME-тип не имеет большого значения. Дополнительную инфор­
мацию о MIME-типах см. в разделе 19.10.
String contentType =
"application/x-java-serialized-object";
response.setContentType(contentType);
2. Создание ObjectOutputStream.
ObjectOutputStream out =
new ObjectOutputStreain(response.getOutputStream());
3. Запись структурированных данных с помощью метода writeObject. Боль­
шинство встроенных структур данных могут быть переданы посредством
w r i t e O b j e c t . Однако классы, написанные вами, должны реализовывать ин­
терфейс S e r i a l i z a b l e . Выполнить данное требование несложно, поскольку в
интерфейсе S e r i a l i z a b l e не объявлены методы. Достаточно лишь указать
интерфейс в определении класса.
S o m e C l a s s v a l u e = new SomeClass(...);
out.writeObject(value) ;
4. Принудительная передача клиенту содерлсимого буфера.
out.flush()/
В следующем разделе приведен пример НТТР-туннелирования.
21.4. Программа просмотра запросов... 1001

21.4. Программа просмотра запросов,


использующая сериализацию объектов
и НТТР-туннелирование
Некоторых пользователей интересует, какие типы запросов приходят на основ­
ные поисковые серверы. Иногда это праздное любопытство. ("А правда ли, что 64%
запросов на AltaVista приходит от работодателей, которые ищут программистов на
Java?") В других случаях такие вопросы задают разработчики HTML-документов, ко­
торые стараются упорядочить их структ)ру так, чтобы они лучше обрабатывались по­
исковыми серверами.
В данном разделе рассматривается система, состоящая из аплета и сервлета, кото­
рая отображает действия поискового "суперсервера", обновляя примеры запросов в
составе специальной Web-страницы. В листинге 21.5 показан код главного аплета, ко­
торый использует дополнительный класс (листинг 21.6) для обновления запросов по­
средством потока, выполняемого в фоновом режиме. После запуска процесса пользо­
вателем аплет два раза в сек)^нду помещает пример запроса в текстовую область
(рис. 21.3). В листинге 21.7 представлен код сервлета, генерирующего запросы. Он
собирает последние запросы, полученные от пользователей, и передает 50 из них
клиенту. Подробно о принципах работы сервлетов см. в главе 19.
Если вы скопируете исходный код аплета и сервлета с сервера h t t p : / / w w w .
c o r e w e b p r o g r a m m i n g . com/ и попытаетесь запустить их, помните, что они будут ра­
ботать только в том случае, если HTML-страница верхнего уровня загружается по
протоколу H T T P . Если вы откроете документ с диска, попытка аплета установить со­
единение с узлом, с которого он был загружен, окончится неудачей.

Листинг 2 1 . 5 . S h o w Q u e r i e s . j a v a

i m p o r t J a v a , a p p l e t .Applets-
import j a v a . a w t . ^ ;
import J a v a . a w t . e v e n t . * ;
im.port j a v a . n e t . * ;

/** Аплет ч и т а е т массивы с т р о к в с о с т а в е Q u e r y C o l l e c t i o n


* и помещает их в текстовую о б л а с т ь с возможностью
* прокрутки. Q u e r y C o l l e c t i o n получает строки через
* входной п о т о к , предназначенный для п е р е д а ч и
*" сериализованных о б ъ е к т о в .
*/
public class ShowQueries extends Applet
implements ActionListener, Runnable {
private TextArea queryArea;
private Button startButton, stopButton, clearButton;
private QueryCollection currentQueries;
private QueryCollection nextQueries;
private boolean isRunning = false;
private String address =
"/servlet/cwp.QueryGenerator";
private URL currentPage;
1002 Глава 2 1 . Аплеты как интерфейс.

public void initO {


setBackground(Color.white);
setLayout(new BorderLayout());
queryArea = new TextArea();
queryArea.setFont(new Font("Serif", Font.PLAIN, 14));
add(queryArea, BorderLayout.CENTER);
Panel buttonPanel = new Panel();
Font buttonFont = new Font("SansSerif", Font.BOLD, 16);
startButton = new Button("Start");
startButton.setFont(buttonFont);
StartButton.addActionListener(this);
buttonPanel.add(startButton);
stopButton = new Button("Stop");
stopButton.setFont(buttonFont);
StopButton.addActionListener(this);
buttonPanel.add(stopButton);
clearButton = new Button("Clear TextArea");
clearButton.setFont(buttonFont);
clearButton.addActionListener(this);
buttonPanel.add(clearButton);
add(buttonPanel, BorderLayout.SOUTH);
currentPage = getCodeBase();
// Примеры запросов. Они загружаются в отдельном
// потоке в фоновом режиме. Перед тем как использовать
// строки, аплет проверяет, закончилась ли загрузка.
currentQueries = new QueryCollection(address, currentPage);
nextQueries = new QueryCollection(address, currentPage);

/** После щелчка на кнопке Start система запускает


* фоновый поток и отображает запросы в
* текстовой области. После щелчка на кнопке Stop
* процесс останавливается. Кнопка Clear TextArea
* предназначена для очистки текстовой области.

public void actionPerforraed(ActionEvent event) {


if (event.getSource() == startButton) {
if (!isRunning) {
Thread queryDisplayer = new Thread(this);
isRunning = true;
queryArea.setText("");
queryDisplayer.start ();
showStatus("Started display thread...");
} else {
showStatus("Display thread already running...
}
} else if (event.getSource() == stopButton) {
isRunning = false;
showStatus("Stopped display thread...");
} else if (event.getSource() == clearButton) {
queryArea.setText("") ;
}
}
21.4. Программа просмотра запросов... 1003

/** Фоновый поток получает объект currentQueries


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

public void run() {


while(isRunning) {
showQueries(currentQueries);
currentQueries = nextQueries;
nextQueries = new QueryCollection(address, currentPage);
}

private void showQueries(QueryCollection queryEntry) {


// Если ответ на обращение к серверу не получен,
// опрашивать каждую секунду. Такая ситуация
// возможна, если пропускная способность линии мала
// либо если нагрузка на сервер велика,
while(!queryEntry.isDoneО) {
showStatus("Waiting for data from server...");
pause(1);
}
showStatus("Received data from server...");
String[] queries = queryEntry.getQueries();
String linefeed = "\n";
// Каждые полсекунды строка помещается в TextArea.
for(int i=0; i<queries.length; i++) {
if (!isRunning) {
return;
}
queryArea.append(queries[i]);
queryArea.append(linefeed);
pause(0.5);
}
}
public void pause(double seconds) {
try {
Thread.sleep((long)(seconds*1000));
} catch(InterruptedException ie) {}

Листинг 21.6.QueryCollection.Java

import java.net.*;
import java.io.*;

/** После создания класса значение возвращается немедленно,


* isDone возвращает false, а getQueries - null.
* В это время запускается поток, предназначенный для
1004 Глава 2 1 . Аплеты как интерфейс.

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


* ObjectlnputStream. После получения данных они
* размещаются в массиве, ссылку на который возвращает
* метод getQueries, а значение флага isDone устанавливается
* равным true.
* Данный класс используется аплетом ShowQueries.

public class QueryCollection implements Runnable {


private String[] queries;
private String[] tempQueries;
private boolean isDone = falser-
private URL dataURL;

public QueryCollection(String urlSuffix, URL currentPage) {


try {
// Необходимо определить лишь завершающую часть URL.
// Остальные компоненты формируются на базе URL
// текущей страницы.
String protocol = currentPage.getProtocol{);
String host = currentPage.getHost();
int port = currentPage.getPort0;
dataURL = new URL(protocol, host, port, urlSuffix);
Thread queryRetriever = new Thread(this);
queryRetriever.start() ;
} catch(MalformedURLException mfe) {
isDone = true;
}

public void run() {


try {
tempQueries = retrieveQueries();
queries = tempQueries;
} catch(lOException ioe) {
tempQueries = null;
queries = null;
}
isDone = true;
}

public String[] getQueries () {


return(queries);
}
public boolean isDone() {
return(isDone);
}
private String[] retrieveQueries() throws lOException {
URLConnection connection = dataURL.openConnection();
// Броузер не должен кэшировать данный URL.
connection.setUseCaches(false);
// Объект ObjectlnputStream позволяет получить
// сразу все компоненты String[].
ObjectlnputStream in =
2 1 . 4 . Программа просмотра запросов. 1005

new ObjееtInputstream(connection.getInputStream())/
try {
// Метод readObject возвращает значение Object,
// поэтому необходимо выполнить приведение типов.
String[] queryStrings = (String[])in.readObject();
return(queryStrings);
} catch(ClassNotFoundException cnfe) {
return(null);
}

Щ Search Engine Query Viemm - Mictostrft trAernei EvpltM


ЕВЩ:
£ite Edrt Vievv F§voftles lock Hetp

~3
Search Engine Queiy Viewer
Interested in what other people are searching for on super-search-engine.com? Press "Start" in the viewer
below to see a random selection of the most recent queries.

Where can I get data on server-side Java?! "3


Where can I get references about server-side Java?
jWhere can I get information about server-side Java?!
jWhere can I find data about servlet programming!!!?
JHow can I get information about JavaServer Pages?
jWhere can I look for information concerning server-side Java?!!!?
JHow can I get resources on servlet programming
JHow can I look for resources about servlet progranuning?!
JWhere can I get references on server-side Java?
jWhere can I get resources about Core Servlets and JSP?!
Where can I get resources about JavaServer Pages?!!!?
jHow can I get information about the book Core Servlets and JavaServer Pages?
Where can I look for data on JavaServer Pages?!
How can I look for references about Core Servlets and JSP?!!!?
JHow can I get data concerning server-side Java?
jWhere can I look for references on server-side Java?
JWhere can I find data about the book Core Servlets and JavaServer Pages?!
JWhere can I look for data about the text Core Servlets and JavaServer Pages?!
JWhere can I find information concerning Core Web Programming (Java 2 Edition)?!
Where can I get data concerning server-side Java?

jStartlj Stop] ClearTextAreaJ

jJ
•;0j Recewed data hom sefver. f g locae'irttane*

Рис. 21.3. Результаты выполнения аплета showQueries

Листинг 2 1 . 7 . Q u e r y G e n e r a t o r . J a v a

package cwp;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.
1006 Глава 2 1 . Аплеты как интерфейс.

/** Сервлет, который генерирует массив строк и передает


* его аплету либо другому клиенту, написанному на Java,
* используя ObjectOutputStream.
V
public class QueryGenerator extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, lOException {
boolean useNumbering = true;
String useNumberingFlag =
request.getParameter{"useNumbering");
if ((useNumberingFlag == null) I|
useNumberingFlag.equals("false")) {
useNumbering = false;
}
String contentType =
"application/x-java-serialized-object";
response.setContentType(contentType);
ObjectOutputStream out =
new ObjectOutputStream(response.getOutputStream());
String[] queries ~ getQueries(useNumbering);
// Если вы передаете нестандартный объект, проверьте,
// указано ли в определении соответствующего класса
// "implements Serializable".
out.writeObject(queries);
out.flush 0 ;
}

public void doPost(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, lOException {
doGet(request, response);
}
private String[] getQueries(boolean useNumbering) {
String[] queries = new String[50];
for(int i=0; i<queries.length; i++) {
queries[i] = randomQuery();
if (useNumbering) {
queries [i] = "" -f (i + 1) + ": " + queries [i];
}
}
return(queries);
}
// "Реальные" запросы пользователей

private String randomQuery() {


String[] locations = { "Where ", "How " };
String[] actions =
{ "can I look for ", "can I find ", "can I get " };
String[] sources =
{ "information ", "resources ", "data ", "references " };
String[] prepositions = { "on ", "about ", "concerning " };
String[] subjects =
21.5. Использование запроса POST... 1007

{ "the book Core Servlets and JavaServer Pages",


"the text Core Servlets and JavaServer Pages",
"Core Servlets and JavaServer Pages",
"Core Servlets and JSP",
"the book Core Web Programming (Java 2 Edition)",
"Core Web Programming (Java 2 Edition)",
"servlet programming", "JavaServer Pages", "JSP",
"Java alternatives to CGI", "server-side Java" };
String[] endings = { "?", "?", "?", "?!", "?!!!?" };
String[][] sentenceTemplates =
{ locations, actions, sources,
prepositions, subjects, endings };
String query = "";
for(int i=0; i<sentenceTemplates.length; i++) {
query = query + randomEntry(sentenceTemplates[i]);
}
return(query);
}

private String randomEntry(String[] strings) {


int index = (int)(Math.random()^strings.length);
return(strings[index]);
}
}

21.5. Использование запроса POST


и непосредственная обработка
результатов (НТТР-туннелирование)
п р и использовании запроса GET аплет может задать отображение результатов
броузером (создать объект URL и вызвать A p p l e t C o n t e x t () . showDocument) либо
обрабатывать ответ самостоятельно (создать объект URL, получить объект
U R L C o n n e c t i o n , открыть входной поток и читать результаты). Этот вопрос рассмат­
ривался в разделах 21.1 и 21,3. В случае запроса POST возможна только обработка дан­
ных аплетом. Основные недостатки запроса POST заключаются в том, что программа
на стороне сервера должна находиться на том же узле, с которого был загружен аплет,
а также в том, что аплет должен самостоятельно обрабатывать полученные данные и
не может передать их броузеру. С другой стороны, данный подход позволяет упро­
стить программу на стороне сервера, поскольку в этом случае не требуется оформлять
результаты в виде HTML-документа, а обновление аплета может происходить без пе­
резагрузки Web-страницы. Кроме того, используя запрос POST, аплет способен не
только принимать, но и передавать сериализованные объекты. Это, несомненно, яв­
ляется преимуществом, поскольку сериализация данных упрощает НТТР-туннелиро-
вание и позволяет организовать взаимодействие через брандмауэры, когда установ­
ление непосредственных соединений с использованием гнезд оказывается невоз­
можным. Аплеты, применяющие метод GET, могут выполнять сериализацию инфор­
мации, но не могут передавать ее по сети, поскольку присоединить к URL произволь­
ные двоичные данные невозможно.
1008 Глава 2 1 . Аплеты как интерфейс...

Для передачи данных POST серверу аплет должен выполнить последовательность


действий, описанную ниже. Несмотря на то что передача данных насчитывает двена­
дцать этапов, каждый из них достаточно прост. Для упрощения кода блоки t r y /
c a t c h не приводятся.
1. Создание объекта URL, указывающего на узел, с которого был загружен
аплет. Как и прежде, на данном этапе создается только путь к сервлету, осталь­
ная часть URL определяется автоматически.
URL c u r r e n t P a g e = g e t C o d e B a s e ( ) ;
String protocol = currentPage.getProtocol();
String host = currentPage,getHost();
int port = currentPage.getPort();
S t r i n g u r l S u f f i x --= " / s e r v l e t / S o m e S e r v l e t " ;
URL dataURL =
new URL(protocol, h o s t , p o r t , u r l S u f f i x ) ;
2. Создание объекта URLConnection. Данный объект используется для получе­
ния входного и выходного потоков, с помощью которых осуществляется взаи­
модействие с сервером.
URLConnection c o n n e c t i o n = d a t a U R L . o p e n C o n n e c t i o n ( ) ;
3. Запрет кэширования данных броузером.
connection.setUseCaches(false);
4. Разрешение на передачу данных (а не только на прием).
connection.setDoOutput(true);
5. Создание объекта ByteArrayOutputStream, который осуществляет буфери­
зацию данных, передаваемых на сервер. Конструктор B y t e A r r a y O u t p u t ­
Stream позволяет задавать начальный размер буфера, однако при необходимо­
сти буфер может автоматически увеличиваться.
ByteArrayOutputStream byteStream =
new ByteArrayOutputStream(512);
6. Связывание выходного потока с ByteArrayOutputStream. Для передачи не­
структурированных данных, как правило, применяется объект P r i n t W r i t e r .
Чтобы передавать сериализованные данные, надо вместо P r i n t W r i t e r вос­
пользоваться объектом Obj e c t O u t p u t S t r e a m .
P r i n t W r i t e r out = new P r i n t W r i t e r ( b y t e S t r e a m , true);
7. Запись данных в буфер. Для неструкт)рированных данных применяется ме­
тод p r i n t , для сериализованных объектов — метод w r i t e O b j e c t .
S t r i n g v a i l = URLEncoder.encode(someVail);
S t r i n g v a l 2 = URLEncoder.encode(someVal2)/
S t r i n g d a t a = "paraml=" + v a i l +
"&param2=" -f- v a l 2 ; / / Символ '&' должен быть указан.
21.6. Аплет, который передает данные методом POST 1009

out.print(data); / / Метод p r i n t , a не p r i n t l n .
o u t . f l u s h O ; / / Метод f l u s h должен быть вызван,
/ / поскольку p r i n t l n не и с п о л ь з у е т с я .
8. Установка поля Content-Length. Для запроса POST это поле является обяза­
тельным.
connection.setRequestProperty
{"Content-Length", S t r i n g . v a l u e O f ( b y t e S t r e a m . s i z e ( ) ) ) /
9. Установка поля Content-Type. По умолчанию Netscape устанавливает тип
m u l t i p a r t / f o r m - d a t a , однако для передачи данных необходимо задать зна­
чение a p p l i c a t i o n / x - w w w - f orm-urlencoded, которое используется Internet
Explorer по умолчанию.
connection.setRequestProperty
("Content-Type", "application/x-www-form-urlencoded");
10. Передача данных.
byteStream.writeTo(connection.getOutputStream());
11. Открытие входного потока. Для приема ASCII или двоичных данных обычно
применяется Buf feredReader, а для приема сериализованных Java-объектов—
ObjectInputStream.
BufferedReader in =
new BufferedReader(new InputStreamReader
(connection.getInputStream()));
12. Чтение результатов. Детали этого процесса зависят от типа информации, пе­
редаваемой сервером. В приведенном ниже примере над каждой строкой полу­
ченных данных выполняются некоторые действия.
String line;
while((line = in.readLine()) != null) {
doSomethingWith(line);
}
Как видите, формирование и обработка запроса POST — достаточно длительная и
рутинная процедура, однако последовательность действий легко запоминается и вы­
полняется автоматически. Кроме того, вы можете скопировать пример с узла
www. corewebprogramming. com и использовать его при написании своих программ.
В следующем разделе приводится пример аплета, выполняющего описанную по­
следовательность де11ствий.

21.6. Аплет, который передает данные


методом POST
В листинге 21.8 приведен код аплета, который реализует подход, описанный в
предыдущем разделе. Данный аплет применяет объекты URLConnecticn и B y t e -
1010 Глава 2 1 . Аплеты как интерфейс...

ArrayOutputStream для передачи методом POST данных по адресу, указанному поль­


зователем. Кроме того, этот аплет использует класс L a b e l e d T e x t F i e l d , который
можно скопировать, обратившись по адресу h t t p : / /www. corewebprograraming. com/.
Ha рис. 21.4 показаны результаты передачи данных сервлету ShowParameters.
Этот небольшой сервлет создает Web-страницу, в которой отображаются все пара­
метры, переданные в составе запроса (см. раздел 19.6).

Листинг 21.8. SendPost.java

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
import J a v a . a w t . e v e n t . * ;
import j a v a . n e t . * ;
import j a v a . i o . * ;

/ * * Аплет, который читает параметры firstName, lastName и


* emailAddress, после чего передает их с помощью
* запроса POST.
*/

public class SendPost extends Applet


implements ActionListener {
private LabeledTextField fIrstNameField, lastNameField,
emailAddressField, hostField,
portField, uriField;
private Button sendButton;
private TextArea resultsArea;
URL currentPage;

public void initO {


setBackground(Color.white);
setLayout(new BorderLayout());
Panel inputPanel = new Panel();
inputPanel.setLayout(new GridLayout(9, 1));
inputPanel.setFont(new Font("Serif", Font.BOLD, 14));
fIrstNameField =
new LabeledTextField("First Name:", 15);
inputPanel.add(fIrstNameField);
lastNameField =
new LabeledTextField("Last Name:", 15);
inputPanel.add(lastNameField);
emailAddressField =
new LabeledTextField("Email Address:", 30);
inputPanel.add(emailAddressField);
Canvas separatorl = new Canvas();
inputPanel.add(separatorl);
hostField =
new LabeledTextField("Host:", 15);

// Аплет, загруженный по сети, может устанавливать


// соединение только с сервлетами, расположенными на
// том же узле, с которого был скопирован сервлет.
hostField.getTextField().setEditable(false);
21.6. Аплет, который передает данные методом POST 1011

currentPage = getCodeBase();
// Для аплета, расположенного на локальном диске,
// метод getHost возвращает пустую строку.
String host = currentPage.getHost();
String resultsMessage = "Results will be shown here...";
if (host.length 0 == 0) {
resultsMessage = "Error: you must load this applet\n" +
"from a real Web server via HTTP,\n" +
"not from the local disk usingXn" +
"a »file:' URL. It is fine,\n" +
"however, if the Web server is\n" +
"running on your local system.";
setEnabled(false);
}
hostField.getTextField().setText(host);
inputPanel.add(hostField);
portField =
new LabeledTextField("Port (-1 means default):", 4 ) ;
String portString = String.valueOf(currentPage.getPort());
portField.getTextField().setText(portString);
inputPanel.add(portField);
uriField =
new LabeledTextField("URI:", 40);
String defaultURI = "/servlet/cwp.ShowParameters";
uriField.getTextField0 .setText(defaultURI);
inputPanel.add(uriField);
Canvas separator2 = new Canvas();
inputPanel.add(separator2);
sendButton = new Button("Submit Data");
sendButton.addActionListener(this);
Panel buttonPanel = new Panel();
buttonPanel.add(sendButton);
inputPanel.add(buttonPanel);
add(inputPanel, BorderLayout.NORTH);
resultsArea = new TextAreaO;
resultsArea.setFont(new Font("Monospaced", Font.PLAIN, 14))
resultsArea.setText(resultsMessage);
add(resultsArea, BorderLayout.CENTER);

public void actionPerformed(ActionEvent event) {


try {
String protocol = currentPage.getProtocol();
String host = hostField.getTextField().getText();
String portString = portField.getTextField().getText();
int port;
try {
port = Integer.parseint(portString);
} catch(NumberFormatException nfe) {
port = -1; // Порт no умолчанию, т.е. 80
}
String uri = uriField.getTextField().getText();
URL dataURL = new URL(protocol, host, port, uri);
URLConnection connection = dataURL.openConnection();

// Броузер не должен записывать данный URL в кэш.


1012 Глава 21. Аплеты как интерфейс...

connection.setUseCaches(false);

// Необходимо разрешить передачу данных на сервер.


connection.setDoOutput(true);

ByteArrayOutputStream byteStream =
new ByteArrayOutputStream(512); // При необходимости
// размеры буфера увеличиваются.
// Поток, который записывает данные в буфер
PrintWriter out = new PrintWriter(byteStream, true);

String postData =
"firstName=" + encodedValue(firstNameField) +
"&lastName=" + encodedValue(lastNameField) +
"&emailAddress=" + encodedValue(emailAddressField);

// Запись данных POST в локальный буфер


out.print(postData);
out.flush О ; // Метод flush должен вызываться, так как
// вместо println используется print

// В составе запроса POST должно присутствовать поле


// Content-Length
String lengthString =
String.valueOf(byteStream.size());
connection. setRecgfuestProperty
("Content-Length", lengthString);

// По умолчанию Netscape устанавливает значение поля


// Content-Type, равное multipart/form-data.
// Если необходимо передавать данные, которые были
// введены посредством формы, надо установить значение
// поля, равное application/x-www-form-urlencoded.
// Internet Explorer устанавливает это значение
/ / п о умолчанию. Если вы передаете данные посредством
// потока ObjectOutputStream, надо пропустить данный этап.

connection.setRequestProperty
("Content-Type", "application/x-www-form-urlencoded");

// Вывод данных POST в реальный выходной поток


byteStream.writeTo(connection.getOutputStream());

BufferedReader in =
new BufferedReader(new InputstreamReader
(connection.getlnputStream0));
String line;
String linefeed = "\n";
resultsArea.setText("");
while((line = in,readLine()) ?= null) {
resultsArea.append(line);
resultsArea.append(linefeed);
}
catch(lOException ioe) {
21.6. Аплет, который передает данные методом POST 1013

// Отображение отладочных данных в окне Java Console


System.out.println("lOException: " + ioe);

}
// LabeledTextField представляет собой объект Panel,
// содержащий компоненты Label и TextField.
// Данный метод извлекает текст из TextField,
// кодирует его и возвращает результаты.

private String encodedValue(LabeledTextField field) {


String rawValue = field.getTextField().getText();
return(URLEncoder.encode(rawValue));
}

t-#fT-mii'HifinmffiHna[infff
"^ ^ -./ 31 Й ^- '^ -^ ^ i i ^H
„^i^" Boolyn«fk$ J ' loc«ion"Jhttp //localhost/'SendPoslApplet/SendPost html
3
Sending POST Data from Applets

Fii^t Name; |Nfarty

Last Name Ня11

Email Addi-^s;;: lvill@coi-e\vebprogi7umnii]g.com

Host bcnlhost

Poit (-1 means default): -1

URI; I /iservie t. cwp ShowPai^utne teis

<!DOCTYPE HTML PUBLIC " - / / W 3 C / / D T D HTML 4 . 0 T r a n s l t i o n a i / / E N " > ~1


<HTML>
<HEAD><TITLE>Reading A i i R e q u e s t Parekmeters</TITLE></HEAD>
<BODY B G C 0 L 0 R = " # F D F 5 E 6 " >
<H1 ALIGIir=CEMTER>Reading A i l R e q u e s t P a r a i n e t e r 3 < / H l >
<TABLE B0RDER=1 ALIG№=CENTER>
<TR BGCOLOR="#rFAD00">
<TH>Pararoeter Narne<TH>Pararneter V a l u e (s)
<TR><TD >f i r s t Name
<TD>Marty
<TR><TD>ernailAddress
<TD>hail@corewebpr ogr ainming. com
<TR><TD>la3tNanie
<TD>Hail
</TABLE>
</BODY></HTML>

,J
лГ
jj^-sa^as'
>' C3 -г-
Рис, 21.4. Результаты использования SendPost для передачи
данных сервлету ShowParameters методом POST
1014 Глава 2 1 . Аплеты как интерфейс...

21.7. Взаимодействие без участия


HTTP-сервера
Тот факт, что сервлет может поддерживать сетевые соединения только с тем уз­
лом, с которого он был скопирован клиентом, совсем не означает, что при установке
соединения должен указываться тот же порт (для H T T P это обычно порт 80). П р и ра­
боте аплет может создавать гнезда, а также использовать технологии JDBC и RMI для
взаимодействия с программами, выполняющимися на стороне сервера.
Аплеты выполняют данные операции точно так же, как и обычные Java-прог­
раммы. Единственным отличием является ограничение, согласно которому соедине­
ния разрешено устанавливать только с одним конкретным узлом.

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

В ЭТОЙ главе...

• Действия, необходимые для установления соединения


с базой данных.

• Пример получения информации из базы данных.

• Вспомогательные классы, упрощающие


использование JDBC.

• Классы для получения и отображения результатов


обращения к базе данных.

• Интерактивная программа просмотра запросов.

• Заранее скомпилированные запросы.


J1y\ZJ^ZJ

редства JDBC предоставляют стандартную библиотеку поддержки реляцион­

С ных баз данных. С помощью JDBC API вы можете обращаться к разнообразным


базам SQL, используя знакомые вам выражения Java. Важно помнить, что, не­
смотря на то, что JDBC определяет стандартный механизм дост)^па к базам данных,
выполнение транзакций и структуру данных, представляющих результаты запроса,
попытки стандартизовать SQL-синтаксис не предпринимаются. Поэтому вы можете
использовать любые SQL-расширения, поддерживаемые базой данных. Н о поскольку
разработчики большинства баз реализуют стандартные средства SQL, то для исполь­
зования баз, расположенных на других узлах, по другим портам и даже для работы с
базами других производителей потребуются лишь минимальные изменения кода.
Официально аббревиа1ура JDBC никак не расшифровывается, но неофициально
считается, что она означает Java Database Connectivity. Несмотря на то что подробное
рассмотрение программирования систем управления базами данных выходит за рам­
ки этой книги, в этой главе мы обсудим основные вопросы использования JDBC. П р и
этом предположим, что вы уже знакомы с основами языка SQL. Подробную информа­
цию о JDBC вы наР1дете по адресу h t t p : / / j a v a . s u n . c o m / p r o d u c t s / j d b c / ; инте­
рактивная документация по API J a v a . s q l и учебные материалы по JDBC расположе­
ны по адресу h t t p : / / j a v a . s u n . c o m / d o c s / b o o k s / t u t o r i a l / j d b c / . Если решае­
мые вами задачи не предполагают использования больших баз данных, возможно, вам
пригодится mySQL. Средства поддержки mySQL для операционных систем, отличных
от Windows, распространяются свободно, кроме того, они могут бесплатно использо­
ваться в систсхме Windows для обучения и проведения исследований. Подробную ин­
формацию о mySQL вы найдете на сервере h t t p : //www . m y s q l . com/.

2 2 . 1 . Основные этапы работы с JDBC


Для организации запроса к базе данных надо выполнить следующие действия.
L ЗагрузитьJDBC-драйвер.
2. Определить URL для установления соединения.
1018 Глава 22. JDBC

3. Установить соединение.
4. Создать выражение (объект Statement).
5. Выполнить запрос.
6. Обработать результаты.
7. Закрыть соединение.
Ниже эта процедура описана детально.

Загрузка драйвера
Драйвер — это программа, которая позволяет взаимодействовать с сервером баз
данных. Для загрузки драйвера надо загрузить соответствующий класс; средства клас­
са, объявленные как s t a t i c , автоматически создают экземпляр драйвера и регистри­
руют его посредством диспетчера драйверов JDBC. Для того чтобы сделать код более
гибким, надо избегать непосредственного использования имени класса.
Последняя рекомендация обязательно вызовет вопросы. Во-первых, как, спросите
вы, загрузить класс, не создавая его экземпляр? Во-вторых, вам, наверное, непонятно,
как ссылаться на класс, имя которого неизвестно на этапе компиляции. На оба вопро­
са можно дать один ответ: необходимо использовать метод C l a s s . forName. Этому
методу передается строка, представляющая полное имя класса (т.е. имя с указанием
пакета), после чего метод загружает указанный класс. П р и выполнении данного мето­
да может генерироваться исключение C l a s s N o t F o u n d E x c e p t i o n , поэтому его вызов
надо поместить в блок t r y / c a t c h . П р и м е р кода, в котором использован данный под­
ход, приведен ниже.
try {
Class.forName{"connect.microsoft.MicrosoftDriver");
Class.forName("oracle.jdbc.driver.OracleDriver");
Class.forName("com.Sybase.jdbc.SybDriver");
} catch(ClassNotFoundException cnfe) {
System.err.println("Error loading driver: " + cnfe);
}

Преимуществом JDBC является тот факт, что сервер баз данных для различных
приложений остается неизменным. Вместо этого JDBC-драйвер (расположенный на
стороне клиента) преобразует вызовы, написанные на языке Java, в специальный
формат, поддерживаемый сервером. Чтобы обеспечить работу с драйвером, специ­
фическим для конкретной базы данных, надо найти в документации полное имя клас­
са. Большинство производителей баз данных поставляют JDBC-драйверы для своих
продуктов; для других баз драйверы создаются независимыми производителями.
Исчерпывающий список доступных драйверов вы найдете по адресу h t t p : / /
i n d u s t r y . j a v a . s u n . c o m / p r o d u c t s / j d b c / d r i v e r s . Многие разработчики бес­
платно предоставляют оценочные версии драйверов (обычно в них ограничены срок
использования или число одновременных соединений), эти версии можно применять
для изучения JDBC.
Метод C l a s s . forName можно применять для загрузки любого класса, доступного
посредством CLASS PATH. На практике большинство производителей поставляют
JDBC-драйверы в составе JAR-файлов, поэтому необходимо следить за тем, чтобы со-
2 2 . 1 . Основные этапы работы с JDBC 1019

ответствующий JAR-файл был указан в переменной окружения CLASS PATH. Как пра­
вило, серверы, поддерживающие сервлеты и JSP, автоматически добавляют к
CLASSPATH JAR-файлы, содержащиеся в каталоге l i b . Поэтому для программ, выпол­
няющихся на стороне сервера и использующих JDBC, JAR-файлы, содержащие драй­
веры, необходимо помещать в каталог l i b сервера.

Определение иЯкдля соединения


Загрузив JDBC-драйвер, надо указать расположение сервера баз данных. В URL,
ссылающихся на базы данных, указывается протокол j d b c : . Кроме того, в составе
URL содержится имя узла, на котором расположен сервер, порт и имя базы данных.
Конкретный формат описывается в документации на драйвер. Примеры URL приве­
дены ниже.
String host = "dbhost.yourcompany.com";
S t r i n g dbName = "someName";
int p o r t = 1234;
String oracleURL = "jdbc:oracle:thin:@" + host +
":" + port + ":" + dbName;
String sybaseURL = "jdbc:Sybase:Tds:" + host +
":" 4- port + ":" + "?SERVICENAME=" + dbName;
Как правило, JDBC применяется при работе сервлетов или обычных приложений,
однако в некоторых случаях данные средства используются аплетами. Используя JDBC в
аплетах, помните, что броузеры позволяют аплетам устанавливать соединение только с
теми узлами, с которых они были загружены. Следовательно, для того, чтобы база дан­
ных была доступна аплет)% сервер баз данных должен быть расположен на том же ком­
пьютере, что и HTTP-сервер. Если добиться этого невозможно, необходимо использо­
вать proxy-сервер, который будет перенаправлять запросы требуемым узлам.

Установление соединения
Для того чтобы установить реальное сетевое соединение, надо, как показано в при­
веденном ниже примере, передать URL, имя пользователя базы данных и пароль методу
g e t C o n n e c t i o n класса DriverManager. Так как при выполнении g e t C o n n e c t i o n мо­
жет генерироваться исключение S Q L E x c e p t i o n , вызов этого метода необходимо по­
местить в блок t r y / c a t c h . Поскольку методы, вызываемые в приведенном примере,
могуо' генерировать то же прерывание, весь фрагмент надо расположить внутри блока
try/catch.
String username = " j a y _ d e b e s e e " ;
String password = " s e c r e t " ;
Connection c o n n e c t i o n =
DriverManager.getConnection(oracleURL, username, password);
Ha данном этапе также может выполняться поиск информации о базе данных с помо­
щью метода g e t M e t a D a t a класса C o n n e c t i o n . Данный метод возвращает объект
D a t a b a s e M e t a D a t a , содержащий методы для определения имени и версии базы данных
( g e t D a t a b a s e P r o d u c t N a m e , g e t D a t a b a s e P r o d u c t V e r s i o n ) и драйвера ( g e t D r i v e r Name,
g e t D r i v e r V e r s i o n ) . Пример поиска сведений о базе приведен ниже.
1020 Глава 22. JDBC

DatabaseMetaData dbMetaData = c o n n e c t i o n . g e t M e t a D a t a ( ) ;
S t r i n g productName =
dbMetaData.getDatabaseProductName();
S y s t e m . o u t . p r i n t l n ( " D a t a b a s e : " + productName);
S t r i n g productVersion =
dbMetaData.getDatabaseProductVersion();
System.out.println("Version: " + productVersion);
Кроме того, в классе Connection содержатся методы p r e p a r e S t a t e m e n t
(создает объект P r e p a r e d S t a t e m e n t , который будет рассматриваться в разделе
22.6), p r e p a r e C a l l (создает объект C a l l a b l e S t a t e m e n t ) , r o l l b a c k (отменяет дей­
ствия, выполненные с момента завершения последней транзакции), commit
(завершает транзакцию), c l o s e (закрывает соединение), i s C l o s e d (определяет, бы­
ло ли соединение закрыто явно или по тайм-ауту).

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


Объект S t a t e m e n t создается посредством объекта Connection и используется
для передачи базе данных запросов и команд, например:
Statement statement = c o n n e c t i o n . c r e a t e S t a t e m e n t ( ) ;

Выполнение запроса
Имея объект S t a t e m e n t , его можно использовать для передачи SQL-запросов базе
данных посредством метода executeQuery. Метод executeQuery возвращает на­
бор результатов (объект R e s u l t S e t ) . Пример получения набора результатов приве­
ден ниже.
S t r i n g query = "SELECT c o l l , c o l 2 , col3 FROM sometable";
ResultSet r e s u l t S e t = s t a t e m e n t . e x e c u t e Q u e r y ( q u e r y ) ;
Для того чтобы внести изменения в базу данных, надо вместо executeQuery вы­
звать метод e x e c u t e Update, передавая ему строку запроса, содержащую команду
UPDATE, INSERT или DELETE. Помимо executeQuery и executeUpdate в классе
Statement определены методы e x e c u t e (выполнение произвольной команды) и
QueryTimeout (установка максимального времени ожидания результатов). Кроме то­
го, вы можете создавать параметризованные запросы, в которых значения передают­
ся заранее скомпилированному запросу фиксированного формата. Этот вопрос будет
рассмотрен в разделе 22.6.

Обработка результатов
Обрабатывать результаты проще всего, перебирая строки (записи) набора с по­
мощью метода n e x t объекта R e s u l t S e t . Для обработки строки класс R e s u l t S e t
предоставляет различные методы qetXxx. В качестве параметра такому методу пере­
дается индекс, либо имя столбца (поля); в результате выполнения метода возвраща­
ются различные типы данных. Например, если возвращаемое значение должно быть
целым числом, надо использовать метод g e t l n t ^ для получения объекта S t r i n g —
метод g e t S t r i n g и т.д. Если вам необходимо отображать результаты, независимо от
типа столбца надо вызывать метод g e t S t r i n g . Если вы передаете методу индекс, еле-
2 2 . 1 . Основные этапы работы с JDBC 1021

дует помнить, что, согласно соглашению SQL, нумерация столбцов начинается не с О,


как для массивов, векторов и других структур данных, а с 1.
Внимание!!

Первый столбец ResultSet имеет индекс 1, а не О.

Н и ж е приведен пример, при выполнении которого выводятся три первых столбца


каждой строки ResultSet.
while(resultSet.next О ) {
System.out.println(results.getString(1) + " " +
results.getString(2) + " " +
results.getString(3));
}
В дополнение к методам g e t X x x n n e x t в классе R e s u l t S e t определен ряд полез­
ных методов, в частности f i n d C o l u m n (позволяет определить индекс столбца по
имени), g e t M e t a D a t a (возвращает информацию о R e s u l t S e t в составе объекта
R e s u l t S e t M e t a D a t a ) и was N u l l (был ли при последнем вызове g e t X x x получен SQL-
результат NULL?). Вместо вызова w a s N u l l вы можете непосредственно сравнить воз­
вращаемое значение с n u l l .
Среди перечисленных выше методов особое значение имеет метод g e t M e t a D a t a .
Для того чтобы корректно обработать R e s u l t S e t , надо знать имена, номера и типы
интересующих вас столбцов. Как правило, эта информация известна заранее, однако
для некоторых запросов высокоуровневые сведения о результатах приходится выяснять
в процессе работы программы. Для этого используется класс R e s u l t S e t M e t a D a t a ; он
позволяет определять номера, имена и типы столбцов в составе R e s u l t S e t . В классе
R e s u l t S e t M e t a D a t a содержатся методы g e t C o l u m n C o u n t (он позволяет определить
число столбцов), getColumnName (возвращает имя столбца по индексу; индекс начина­
ется с 1), g e t C o l u m n T y p e (возвращает целочисленное значение, которое можно срав­
нивать с константами, определенными в классе J a v a , s q l . T y p e s ) , i s R e a d O n l y
(указывает, предназначено ли значение только для чтения), i s S e a r c h a b l e (может ли
значение быть использовано в составе выражения WHERE), i s N u l l a b l e (допустимо ли
значение n u l l ) и ряд других методов, позволяющих получить детальную информацию о
данных, содержащихся в столбцах. В составе объекта R e s u l t S e t M e t a D a t a отсутствуют
сведения о числе строк в составе набора результатов; единственный способ выяснить
это — последовательно вызывать метод n e x t класса ResultSet до тех пор, пока он не вер­
нет значение f a l s e .

Закрытие соединения
Для того чтобы закрыть соединение, надо включить в программу следующее вы­
ражение:
connection.close();
Если вы собираетесь выполнять дополнительные операции с базой данной, може­
те закрыть соединение позже, однако следует помнить, что открытое соединение по­
требляет большое количество ресурсов.
1022 Глава 2 2 . JDBC

22.2. Пример использования JDBC


В листинге 22.3 представлен код простого класса F r u i t T e s t , который выполняет
действия, описанные в предыдущем разделе, для отображения таблицы с именем
f r u i t s . Как видно из листингов 22.1 и 22.2, адрес узла, порт, имя базы данных и
драйвер задаются в командной строке. Вместо того чтобы включать в состав класса
имя драйвера и логику для генерации URL базы данных, соответствующий код выне­
сен в отдельный класс с именем D r i v e r U t i l i t i e s , показанный в листинге 22.4. Та­
кой подход локализует изменения, которые необходимо внести при переходе к дру­
гому драйверу.
Данный пример не зависит от того, как именно создавалась база данных; значение
имеет только результирующий формат. В данном случае для создания таблиц также
применялись средства JDBC (см. листинг 22.5). В данный момент вам достаточно
лишь кратко просмотреть листинг, поскольку в нем используются вспомогательные
средства, которые будут обсуждаться в следующем разделе.
Для тех, кто незнаком с пакетами, напомним следующее. Так как класс F r u i t T e s t
содержится в пакете cwp, он располагается в подкаталоге с именем cwp. Перед ком­
пиляцией файла мы включаем в состав переменной окружения CLASS PATH каталог,
содержащий подкаталог cwp. (JAR-файл, содержащий JDBC-драйверы, также должен
быть указан в CLASS PATH.) Для компиляции достаточно сделать каталог cwp текущим
и выполнить команду j a v a c F r u i t T e s t . J a v a . Однако, чтобы запустить F r u i t T e s t ,
необходимо указать пакет, т.е. команда принимает вид j a v a cwp. F r u i t T e s t ....

Листинг 2 2 . 1 . Результаты выполнения F r u i t T e s t (при взаимодействии


с базой данных Oracle в системе Solaris)

Prompt> J a v a c w p . F r u i t T e s t d b h o s t l . a p l . j h u . e d u РТЕ
h a l l xxxx o r a c l e
Database: Oracle
Version: Oracle7 Server Release 7 . 2 . 3 . 0 . 0 - Production Release
PL/SQL R e l e a s e 2 . 2 . 3 . 0 . 0 - P r o d u c t i o n
Comparing A p p l e s and O r a n g e s

QUARTER APPLES APPLESALES ORANGES ORANGESALES TOPSELLER


1 32248 $3547.28 18459 $3138.03 Maria
2 35009 $3850.99 18722 $3182.74 Bob
3 39393 $4333.23 18999 $3229.83 Joe
4 42001 $4620.11 19333 $3286.61 Maria

Листинг 2 2 . 2 . Результаты выполнения F r u i t T e s t (при взаимодействии


с базой данных Sybase в системе NT)

Prompt> J a v a c w p . F r u i t T e s t d b h o s t 2 . a p l . j h u . e d u 605741
h a l l xxxx Sybase
D a t a b a s e : A d a p t i v e S e r v e r Anywhere
Version: 6.0.2.2188
2 2 . 2 . Пример использования JDBC 1023

Comparing Apples and Oranges

quarter apples applesales oranges orangesales topseller


1 32248 $3547.28 18459 $3138.03 Maria
2 35009 $3850.99 18722 $3182.74 Bob
3 39393 $4333.23 18999 $3229.83 Joe
4 42001 $4620.11 19333 $3286.61 Maria

Листинг 22.3.FruitTest.Java

package cwp;

import java.sql.*;

/** Пример использования JDBC. Программа устанавливает


* соединение с базой данных Oracle либо Sybase
* и выводит значение столбцов таблицы fruits.
V
public class FruitTest {

/•• Читает из командной строки адрес узла,


* имя базы данных, имя пользователя, пароль
* и идентификатор производителя. Идентификатор
* используется для загрузки требуемого
* драйвера и определения формата URL. Драйвер,
* URL, имя пользователя, адрес узла и пароль
* передаются методу showFruitTable.
V
public static void main(String[] args) {
if (args.length < 5) {
printUsage();
return;
}
String vendorName = args[4];
int vendor = DriverUtilities.getVendor(vendorName)/
if (vendor == DriverUtilities.UNKNOWN) {
printUsage();
return;
}
String driver = DriverUtilities.getDriver(vendor);
String host = args[0];
String dbName = args[l];
String url = DriverUtilities.makeURL(host, dbName, vendor);
String username = args[2];
String password = args[3];
showFruitTable(driver, url, username, password);

/** Получение таблицы и вывод всех значений. */

public static void showFruitTaUDle(String driver.


1024 Глава 22. JDBC

string url,
String username,
String password) {
try {
// Если драйвер базы данных еще не загружен,
// производится его загрузка.
Class.forName(driver);
// Установление соединения с базой данных.
Connection connection =
DriverManager.getConnection(url, username, password);
// Поиск информации о базе данных в целом.
DatabaseMetaData dbMetedData = connection.getMetaData();
String productName =
dbMetaData.getDatabaseProductName();
System.out.println("Database: " + productName);
String productVersion =
dbMetciData. getDatabaseProductVersion () ;
System.out.println("Version: " + productVersion + " \ n " ) ;
System.out.println("Comparing Apples and Oranges\n" +
"============:=:=========:======:" ) ;
Statement statement = connection.createstatement();
String query = "SELECT * FROM fruits";
// Передача запроса базе данных и сохранение результатов.
ResultSet resultSet = statement.executeQuery(query);
// Поиск информации о конкретной таблице.
ResultSetMetaData resultsMetaData =
resultSet .getMetedData О ;
int coliunnCount = resultsMetaData.getColumnCount0;
// Нумерация столбцов начинается с 1, а не с О.
for(int i=l; i<columnCount+l; i++) {
System, ou t. print (result sMetaData.getColглtlnName (i) +

}
System.out.println();
// Вывод результатов,
while(resultSet.next О ) {
// Квартал
System.out.print(" " + resultSet.getint(1));
// Информация о яблоках
System.out.print(" " + resultSet.getint(2));
// Уровень продаж яблок
System.out.print(" $" + resultSet.getFloat(3));
// Информация об апельсинах
System.out.print(" " + resultSet.getint(4));
// Уровень продаж апельсинов
System.out.print(" $" + resultSet.getFloat(5));
// Ведущий продавец
System.out.println(" " + resultSet.getString(6));
}
} catch(ClassNotFoundException cnfe) {
System.err.println("Error loading driver: " + cnfe);
} catch(SQLException sqle) {
System.err.println("Error connecting: " + sqle);
2 2 . 2 . Пример использования JDBC 1025

)
}

private static void printUsageO {


System.out.println("Usage: FruitTest host dbName " +
"username password oracle I Sybase.") ,
}

Листинг 22.4. DriverUtilities.Java

package cwp;

/** Несколько простых методов для установления


* JDBC-соединения с базами Oracle и Sybase.
* Данный код <1>не относится</1> к программам общего
* назначения, он ориентирован на конкретные условия.
V
public class DriverUtilities {
public static final int ORACLE = 1;
public static final int SYBASE = 2;
public static final int UNKNOWN = -1;

/** Создание URL в формате, требуемом для


* взаимодействия с драйверами Oracle и Sybase.

public static String makeURL(String host. String dbName,


int vendor) {
if (vendor == ORACLE) {
return ("jdbc: oracle: thin: (3" + host + ":1521:" -i- dbName);
} else if (vendor == SYBASE) {
return("jdbc:Sybase:Tds:" + host + ":1521" +
"?SERVICENAME=" + dbName);
} else {
return(null);
}
}

/** Получение полного имени драйвера. */

public static String getDriver(int vendor) {


if (vendor == ORACLE) {
return("oracle.jdbc.driver.OracleDriver");
} else if (vendor == SYBASE) {
return("com.Sybase.jdbc.SybDriver");
} else {
return(null) ;
}

/** Преобразование имени в целочисленное значение. */


1026 Глава 22. JDBC

public static int getVendor(String vendorName) {


if (vendorName.equalsIgnoreCase("oracle")) {
return(ORACLE);
} else if (vendorName.equalsIgnoreCase("Sybase"))
return(SYBASE);
} else {
return(UNKNOWN);
}
}

Листинг 22.5. FruitCreation.java

package cwp;

import java.sql.*;

/** Создание таблицы с именем fruits в базе данных


* Oracle или Sybase.

public class FruitCreation {


public static void main(String[] args) {
if (args.length < 5) {
printUsage();
return;
}
String vendorName = args[4];
int vendor = DriverUtilities.getVendor(vendorName);
if (vendor == DriverUtilities.UNKNOWN) {
printUsage ();
return;
}
String driver = DriverUtilities.getDriver(vendor);
String host = args[0];
String dbName = args[l];
String url =
DriverUtilities.makeURL(host, dbName, vendor);
String username = args[2];
String password = args[3];
String format =
"(quarter int, " +
"apples int, applesales float, " +
"oranges int, orangesales float, " +
"topseller varchar(16))";
String[] rows =
{ "(1, 32248, 3547.28, 18459, 3138 03, 'Maria')",
"(2, 35009, 3850.99, 18722, 3182 74, 'Bob')",
"(3, 39393, 4333.23, 18999, 3229 83, 'Joe')",
"(4, 42001, 4620.11, 19333, 3286 61, 'Maria')" };
Connection connection =
DatabaseUtilities.createTable(driver. url,
username, password.
22.3. Вспомогательные классы для работы с JDBC 1027

"fruits", format, rows,


false);
// Проверка корректности созданной таблицы. Для повь>1шения
// эффективности используется существующее соединение.
DatabaseUtilities.printTable(connection, "fruits",
11, true);

private static void printUsageO {


System.out.println("Usage: FruitCreation host dbName " +
"username password oracle|Sybase.");
}

22.3. Вспомогательные классы для работы


с JDBC
в некоторых приложениях нет необходимости построчно обрабатывать результа­
ты запроса. Н а п р и м е р , в сервлетах и JSP достаточно форматировать данные, полу­
ченные в результате обращения к базе, и преобразовать их в HTML-таблицу, элек­
тронную таблицу Excel либо включить в состав Web-страницы в виде набора строк.
При разработке подобных приложений существенную помощь могут оказать методы,
предназначенные для получения R e s u l t S e t , извлечения данных и хранения их для
последующего отображения.
В данном разделе описываются два класса, решающие описанные выше задачи,
кроме того, методы этих классов позволяют форматировать, отображать данные и
создавать таблицы. П е р в ы й из этих двух классов, D a t a b a s e U t i l i t i e s , содержит
следующие статические методы.
1. getQueryResults
Этот метод устанавливает соединение с базой данных, выполняет запрос, получает
все записи, помещает их в строковый массив и сохраняет в составе объекта
D B R e s u l t s (см. листинг 22.7). Кроме того, этот метод записывает в объект
D B R e s u i t s имя и версию базы данных, имена всех столбцов и объект C o n n e c t i o n .
Существуют два варианта метода g e t Q u e r y R e s u l t s : один из них устанавливает но­
вое соединение, а другой пользуется имеющимся соединением.
2. createTable
Получив имя таблицы, строку, описывающую формат столбцов, и массив строк,
описывающих содержимое таблицы, этот метод устанавливает соединение с
базой данных, удаляет указанную таблицу (если она существует), выполняет ко­
манду CREATE TABLE с учетом заданного формата и передает последователь­
ность команд INSERT INTO для включения в таблицу строк (записей). Как и для
предыд)'щего метода, существуют два варианта c r e a t e T a b l e : один из них ус­
танавливает новое соединение, а другой использует имеющееся.
3. printTable
Данному методу в качестве параметра передается имя таблицы. В процессе ра­
боты метод p r i n t T a b l e устанавливает соединение с указанной таблицей, чи-
1028 Глава 22. JDBC

тает все строки и выводит их в стандартный выходной поток. Для получения


результатов имя таблицы включается в состав запроса SELECT * FROM
tableName и передается методу g e t Q u e r y R e s u l t s .
4. printTableData
Этот метод выводит в стандартный входной поток информацию, записанную в
DBResults при выполнении предыдущего запроса. Метод p r i n t T a b l e D a t a
используется методом p r i n t T a b l e , однако его можно также применять для
отладки.
Код класса D a t a b a s e U t i l i t i e s представлен в листинге 22.6, а код DBResults — в
листинге 22.7. Класс DBResults хранит результаты запроса и возвращает их в виде
массивов строк (getRow) или оформляет как HTML-таблицу (toHTMLTable). В каче­
стве заголовков столбцов HTML-таблицы используются заголовки столбцов таблицы
базы данных, которые отображаются голубым цветом. Приведенные ниже два выра­
жения передают базе данных запрос, получают результаты и форматируют их, пред­
ставляя в виде HTML-таблицы.
DBResults results =
DatabaseUtilities.getQueryResults(driver, url,
username, password,
query, true);
out.println(results.toHTMLTable("CYAN"));
Как и другие исходные тексты, представленные в данной книге, коды классов
DatabaseUtilities и DBResults можно скопировать, обратившись по адресу
www. corewebprogramming. com, и использовать без ограничений.

Листинг 22.6. DatabaseUtilities. Java

package cwp;

import java.sql.*;

public class DatabaseUtilities {

/** Соединение с базой данных, выполнение запроса


* и сохранение результатов в объекте DBRresults.
* Если соединение с базой данных остается открытым
* (параметр close указывает, следует ли закрывать
* соединение), его можно получить посредством
* вызова DBResults.getConnection.
V
public static DBResults getQueryResults(String driver,
String url.
String username.
String password.
String query,
boolean close) {
try {
Class.forName(driver);
Connection connection =
2 2 . 3 . Вспомогательные классы для работы с JDBC 1029

DriverManager.getConnection(url, username, password)


return(getQueryResults(connection, query, close));
catch(ClassNotFoundException cnfe) {
System.err.println("Error loading driver: " + cnfe);
return(null);
catch(SQLException sqle) {
System.err.println("Error connecting: " + sqle);
return(null);

/** Получение результатов, как и в предыдущем случае, но


* вместо установления нового соединение используется
* существующее.

public static DBResults getQueryResults(Connection connection.


String query,
boolean close) {
try {
DatabaseMetaData dbMetaData = connection.getMetaData();
String productName =
dbMetaData.getDatabaseProductName();
String productVersion =
dbMetaData.getDatabaseProductVersion();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query) ;
ResultSetMetaData resultsMetaData =
resultSet.getMetaData();
int columnCount = resultsMetaData.getColumnCount();
String[] columnNames = new String[columnCount];
// Нумерация столбцов начинается с 1, а не с О.
for(int i=l; i<columnCount+l; i++) {
columnNames[i-1] =
resultsMetaData.getColumnName(i).trim();
}
DBResults dbResults =
new DBResults(connection, productName, productVersion,
columnCount, columnNames);
while(resultSet.next 0 ) {
String[] row = new String[columnCount];
// Нумерация начинается с 1.
for(int i=l; i<columnCount+l; i++) {
String entry = resultSet.getString(i);
if (entry != null) {
entry = entry.trim();
}
row[i-l] = entry;
}
dbResults.addRow(row);
}
if (close) {
connection.close() ;
}
return(dbResults);
} catch(SQLException sqle) {
1030 Глава 22. JDBC

System.err.println("Error connecting: " + sqle)


return(null);

/** Построение таблицы заданного формата и включение в нее


* указанных строк.
V
public static Connection createTable(String driver.
String url.
String username,
String password,
String tableName,
String tableFormat,
String[] tableRows,
boolean close) {
try {
Class.forName(driver);
Connection connection =
DriverManager.getConnection(url, username, password);
return(createTable(connection, username, password,
tableName, tableFormat,
tableRows, close));
} catch(ClassNotFoundException cnfe) {
System.err.println("Error loading driver: " + cnfe);
return(null);
} catch(SQLException sqle) {
System.err.println("Error connecting: " + sqle);
return(null);
}

/•• Этот метод выполняет те же действия, что и предыдущий,


* но использует существующее соединение.
V
public static Connection createTable(Connection connection.
String username.
String password.
String tableName,
String tableFormat,
String[] tableRows,
boolean close) {
try {

Statement statement = connection.createStatement();


// Если таблица существует, она удаляется. При
// отсутствии таблицы ошибка не возникает,
// действия выполняются как в блоке try, так
/ / и в блоке catch,
try {
statement.execute("DROP TABLE " + tableName);
} catch(SQLException sqle) {}
String createCommand =
"CREATE TABLE " + tableName + " " + tableFormat;
2 2 . 3 . Вспомогательные классы для работы с JDBC 1031

statement, execute (createCoinmand) ;


String insertPrefix =
"INSERT INTO " + tableName + " VALUES";
for(int i=0; i<tableRows.length; i++) {
statement.execute(insertPrefix + tableRows[i]);
}
if (close) {
connection.close();
return(null);
} else {
return(connection);
}
catch(SQLException sqle) {
System.err.println("Error creating table: " + sqle);
return(null);
}

public static void printTable(String driver,


String url.
String username.
String password.
String tableName,
int entryWidth,
boolean close) {
String query = "SELECT * FROM " + tableName;
DBResults results =
getQueryResults(driver, url, username,
password, query, close);
printTableData(tableName, results, entryWidth, true);
}

/** Вывод данных в виде таблицы. Ширина каждого столбца


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

public static void printTable(Connection connection.


String tableName,
int entryWidth,
boolean close) {
String query = "SELECT * FROM " + tableName;
DBResults results =
getQueryResults(connection, query, close);
printTableData(tableName, results, entryWidth, true);
}

public static void printTableData(String tableName,


DBResults results,
int entryWidth,
boolean printMetaData)
if (results == null) {
return;
}
if (printMetaData) {
System.out.println("Database: " +
1032 Глава 2 2 . JDBC

results.getProductName());
System.out.println("Version: " +
results.getProductVersion());
System.out.println() ;
}
System.out.println(tableName + " : " ) ;
String underline =
padString("", tableName.length()+1, " = " ) ;
System.out.println(underline);
int columnCount = results.getColumnCount();
String separator =
makeSeparator(entryWidth, columnCount);
System.out.println(separator);
String row = makeRow(results.getColumnNames0, entryWidth);
System.out.println(row);
System.out.println(separator);
int rowCount = results.getRowCount0;
for(int i=0; i<rowCount; i++) {
row = makeRow(results.getRow(i), entryWidth);
System.out.println(row);
}
System.out.println(separator);
}
// Строка в формате "| xxx | xxx | xxx I"

private static String makeRow(String[] entries,


int entryWidth) {
String row = "i";
for(int i=0; i<entries.length; i++) {
row = row + padString(entries[i], entryWidth, " " ) ;
row = row + " I";
}
return(row);
}
// Строка в формате "+ + + -f"

private static String makeSeparator(int entryWidth,


int columnCount) {
String entry = padString("", entryWidth+1, " - " ) ;
String separator = "+";
for(int i=0; i<columnCount; i++) {
separator = separator + entry + "+";
}
return(separator);
}
private static String padString(String orig, int size,
String padChar) {
if (orig == null) {
orig = "<null>";
}
// Объект StringBuffer используется для того, чтобы не
// приходилось создавать слишком много объектов String.
StringBuffer buffer = new StringBuffer("");
22.3. Вспомогательные классы для работы с JDBC 1033

int extraChars = size - orig.length();


for(int i=0; i<extraChars; i++) {
buffer.append(padChar);
}
buffer.append(orig) ;
return(buffer.toString());

Листинг 22.7. DBResults . java

package cwp;

import java.sql.*;
import java.util.*;

/** Класс, предназначенный для хранения результатов


* JDBC-запроса. От объекта ResultSet отличается
* следующими особенностями:
* <UL>
* <LI>B объекте ResultSet не обязательно содержатся все
* данные. При обращении за следующей записью может
* быть снова установлено соединение с базой.
* <Ы>Класс DBResults хранит результаты в массивах строк.
* <Ы>Данный класс включает объекты DatabaseMetaData (имя
* и версия базы данных) и ResultSetMetaData
* (имена столбцов).
* < Ы > В классе DBResults предусмотрен метод toHTMLTable,
* который преобразует результаты в строки, содержащие
* код HTML-таблицы.
* </UL>
V
public class DBResults {
private Connection connections-
private String productName;
private String productVersion;
private int columnCount;
private String[] columnNames;
private Vector queryResults;
String[] rowData;

public DBResults(Connection connection,


String productName,
String productVersion,
int columnCount,
String[] columnNames) {
this.connection = connection;
this.productName = productName;
this.productVersion = productVersion;
this.columnCount = columnCount;
this.columnNames = columnNames;
rowData = new String[columnCount];
queryResults = new Vector();
1034 Глава 22. JDBC

public Connection getConnection() {


return(connection);
}
public String getProductName() {
return(productName);
}
public String getProductVersion() {
return(productVersion);

public int getColumnCount() {


return(columnCount);
}
public String[] getColumnNames() {
return(columnNames);
}
public int getRowCount() {
return(queryResults.size());
}
public String[] getRow(int index) {
return((String[])queryResults.elementAt(index));
}
public void addRow(String[] row) {
queryResults.addElement(row);
}
/** Вывод результатов в виде HTML-таблицы.
* Имена столбцов используются в качестве заголовков
* таблицы. Остальные ячейки заполняются данными.

public String toHTMLTable(String headingColor) {


StringBuffer buffer =
new StringBuffer("<TABLE BORDER=l>\n");
if (headingColor != null) {
buffer.append(" <TR BGCOLOR=\"" + headingColor +
"\">\n ");
} else {
buffer.append(" <TR>\n ");
}
for(int col=0; col<getColumnCount(); col++) {
buffer.append("<TH>" + columnNames[col]);
}
for(int row=0; row<getRowCount(); row++) {
buffer.append("\n <TR>\n " ) ;
String[] rowData = getRow(row);
for (int col=0; coKgetColumnCount () ; col++) {
buffer.append("<TD>" + rowData[col]);
22.4. Применение класса DatabaseUtilities 1035

}
}
buffer.append("\n</TABLE>");
return(buffer.toString());
}
}

22.4. Применение класса DatabaseUtilities


Сейчас мы покажем, как класс D a t a b a s e U t i l i t i e s , описанный в разделе 22.3,
помогает получать и отображать данные из базы. В листинге 22.8 показан класс, кото­
рый обращается к базе данных, указанной в командной строке, и выводит все строки
таблицы employees. В листингах 22.9 и 22.10 показаны результаты обращения про­
граммы к базам Oracle и Sybase. В листинге 22.11 приведен тот же класс, который об­
ращается к той же базе, но выводит результаты в виде HTML-таблицы. В листинге
22.12 представлен сгенерированный HTML-документ. В листинге 22.13 показан JDBC-
код, используемый для создания таблицы employees.

Листинг 22.8. EmployeeTest.Java

package cwp;
import j a v a . s q l . * ;
/** Соединение с базой Oracle или Sybase и вывод
* таблицы employees.
V
p u b l i c c l a s s EmployeeTest {
p u b l i c s t a t i c void m a i n ( S t r i n g [ ] args) {
i f ( a r g s . l e n g t h < 5) {
printUsage ( ) ;
return;
}
S t r i n g vendorName = a r g s [ 4 ] ;
i n t vendor = D r i v e r U t i l i t i e s . g e t V e n d o r ( v e n d o r N a m e ) ;
i f (vendor == DriverUtilities.UNKNOWN) {
printUsage ( ) ;
return;
}
String driver = DriverUtilities.getDriver(vendor);
S t r i n g host = a r g s [ 0 ] ;
S t r i n g dbName = a r g s [ l ] ;
String url =
D r i v e r U t i l i t i e s . m a k e U R L ( h o s t , dbName, v e n d o r ) ;
S t r i n g username = a r g s [ 2 ] ;
S t r i n g password = a r g s [ 3 ] ;
DatabaseUtilities.printTable(driver, url,
username, pas sword,
"employees", 12, true)
}
1036 Глава 22. JDBC

private static void printUsageO {


System.out.println("Usage: EmployeeTest host dbName " +
"username password oracle I Sybase.");
}

Листинг 22.9. Результаты выполнения EmployeeTest (взаимодействие с базой


Oracle в системе Solaris)

Prompt> Java cwp.EmployeeTest dbhostl.apl.jhu.edu РТЕ


hall xxxx oracle
Database: Oracle
Version Oracle? Server Release 7.2.3.0.0 - Production Release
PL/SQL Release 2 2.3.0.0 - Production
employees:

I ID I FIRSTNAME | LASTNAME | LANGUAGE | SALARY I

1 Wye Tukay COBOL 1 42500


2 Britt Tell C++ 1 62000
3 Max Manager none 1 15500
4 Polly Morphic Smalltalk | 51500
5 Frank Function Common Lisp | 51500
6 Justin Timecompiler Java 1 98000
7 Sir Viet Java 1 114750
8 I Jay
Jay Espy Java 1 128500

Листинг 22.10. Результаты выполнения EmployeeTest (взаимодействие с


базой Sybase в системе NT)

Prompt> Java cwp.EmployeeTest dbhost2.apl.jhu.edu 605741


hall xxxx Sybase
Database: Adaptive Server Anywhere
Version: 6.0.2.2188
employees:

1 id 1 firstname | lastname | language | salary 1

1 1 1 Wye I Tukay | COBOL | 42500.0 I


i 2 1 Britt 1 Tell 1 C++ 1 62000.0 1
1 3 1 Max 1 Manager | none | 15500.0 1
1 4 i Polly 1 Morphic 1 Smalltalk | 51500.0 1
1 5 1 Frank 1 Function | Common Lisp 1 51500.0 1
1 6 1 Justin 1Timecompiler | Java | 98000.0 1
1 7 1 Sir 1 Viet 1 Java i 114750.0 1
1 8 1 Jay 1 Espy 1 Java | 128500.0 1
+ +_. + + +__. +
22.4. Применение класса DatabaseUtilities 1037

Листинг 22.11. EmployeeTest2 . java

package cwp;

import java.sql.*;

/** Соединение с базой Oracle или Sybase и вывод данных


* из таблицы employees в формате HTML-таблицы.
Ч
public class EmployeeTest2 {
public static void main(String[] args) {
if (args.length < 5) {
printUsage();
return;
}
String vendorName = args[4];
int vendor = DriverUtilities.getVendor(vendorName);
if (vendor == DriverUtilities.UNKNOWN) {
printUsage();
return;
}
String driver = DriverUtilities.getDriver(vendor);
String host = args[0];
String dbName = args[l];
String url =
DriverUtilities.makeURL(host, dbName, vendor);
String username = args[2];
String password = args[3];
String query = "SELECT * FROM employees";
DBResuits results =
DatsJsaseUtilities.getQueryResuits(driver, url,
username, password,
query, true);
System.out.println(results.toHTMLTable("CYAN"));
}
private static void printUsage() {
System.out.println("Usage: EmployeeTest2 host dbName " +
"username password oracle I Sybase.");
}

Листинг 22.12. Результаты выполнения EmployeeTest2 (взаимодействие с


базой Sybase в системе NT)

Prompt> Java cvф.EmployeeTвst2 dbhost2 605741


hall xxxx Sybase
<TABLE B0RDER=1>
<TR BGCOLOR="CYAN">
<TH>id<TH>firstname<TH>lastname<TH>language<TH>salary
<TR>
1038 Глава 22. JDBC

<TD>l<TD>Wye<TD>Tukay<TD>COBOL<TD>42500.О
<TR>
<TD>2<TD>Britt<TD>Tell<TD>C++<TD>62000.0
<TR>
<TD>3<TD>Max<TD>Manager<TD>none<TD>15500.0
<TR>
<TD>4<TD>Polly<TD>Morphic<TD>Sinalltalk<TD>51500.0
<TR>
<TD>5<TD>Frank<TD>Function<TD>Common Lisp<TD>51500.0
<TR>
<TD>6<TD>Justin<TD>Timecompiler<TD>Java<TD>98000.0
<TR>
<TD>7<TD>Sir<TD>Vlet<TD>Java<TD>114750.0
<TR>
<TD>8<TD>Jay<TD>Espy<TD>Java<TD>128500.0
</TABLE>

Листинг 22.13.EmployeeCreation.Java

package cwp;

import java.sql.*;

/** Создание простой таблицы employees с использованием


* DatabaseUtilities.

public class EmployeeCreation {


public static Connection createEmployees(String driver.
String url,
String username,
String password,
boolean close) {
String format =
"(id int, firstname varchar(32), lastname varchar(32),
"language varchar(16), salary float)";
String[] employees =
{"(1, 'Wye', 'Tukay', 'COBOL', 42500)",
"(2, 'Britt', 'Tell', 'C++', 62000)",
"(3, 'Max', 'Manager', 'none', 15500)",
"(4, 'Polly', 'Morphic', 'Smalltalk', 51500)",
"(5, 'Frank', 'Function', 'Common Lisp', 51500)",
"(6, 'Justin', 'Timecompiler', 'Java', 98000)",
"(7, 'Sir', 'Viet', 'Java', 114750)",
"(8, 'Jay', 'Espy', 'Java', 128500)" } ;
return(DatabaseUtilities.createTsuDle(driver, url,
username, password,
"employees",
format, employees,
close));

public static void main(String[] args) {


22.5. Интерактивная программа просмотра запросов 1039

if (args.length < 5) {
printUsage();
return;
}
String vendorName = args[4];
int vendor = DriverUtilities.getVendor(vendorName);
if (vendor == DriverUtilities.UNKNOWN) {
printUsage();
return;
}
String driver = DriverUtilities.getDriver(vendor);
String host = args[0];
String dbName = args[l];
String url =
DriverUtilities.makeURL(host, dbName, vendor);
String username = args[2];
String password = args[3];
createEmployees(driver, url, username, password, true);

private static void printUsage() {


System.out.println("Usage: EmployeeCreation host dbName
"username password oracle|Sybase.");
}

22.5. Интерактивная программа просмотра


запросов
До сих пор при обращении к базам данных структура запроса была известна на
этапе написания кода. Однако в реальных приложениях запрос часто создается на ос­
нове данных, введенных пользователем, которые становятся известны только в мо­
мент выполнения программы. В некоторых случаях формат запроса остается фикси­
рованным, изменяются только конкретные значения. Для реализации таких запросов
можно использовать заранее подготовленные выражения, которые будут обсуждаться
в разделе 22.6. В других случаях ф о р м а т запросов может изменяться. П р и обработке
подобных данных применяется класс R e s u l t S e t M e t a D a t a , который позволяет оп­
ределить число столбцов в составе объекта R e s u l t S e t , их имена и типы данных.
Класс R e s u l t S e t обсуждался в разделе 22.1. Класс D a t a b a s e U t i l i t i e s , код которо­
го представлен в листинге 22.6, сохраняет метаданные в составе объекта D B R e s u i t s ;
для их получения вызывается метод s h o w Q u e r y D a t a . Благодаря доступу к метадан­
ным появляется возможность создать интерактивную программу просмотра запросов
( Q u e r y V i e w e r ) , и н т е р ф е й с которой показан на рис. 22.1-22.5. Коды, позволяющие
получить данный результат, рассматриваются далее в этом разделе.
1040 Глава 2 2 . JDBC

{шввшишвтяшшшшшшшшшшшш^шт
: ^ Of*cl« С Sybase

U«^narw. 1 Pa)5$wo«f; |

Ousry: 1

SfKi^Resciltg 1

Рис. 22.1. Начальное окно программы

Ou«iyOa!a •- ;-•

Ho«t jdbhosti apl jhu edu DBName:(PTE Ui^f <^ Orid« г $уЬдб» 1

Ш«тдт#: jhaii Pa«ewo«J: j * * * * * * *

Ouery. JSELECT * FROM employees

IrisiivowRie^
1 b en 1 rrm-.frniVi ffiiTi rttir». muTifi

- <;iuaiyfllesu{t9
iD 1 RftSTHAME J LASTHAtiE { LANGUAGE \ S«.ARy II
1 Wye Tukay COBOL 42500

1 2 Britt Tell C++ 62000


3 Max Manager none 15500 11
4 Polly Morphic Smalltalk 51500
5 Frank Function Common Lisp 51500 i1
6 Justin Time compiler Java 98000
;J
7 Sir Viet Java 114750 И

8 Jay Espy Java 128500


"-' '" ' 1
Рис. 22.2. Данные, отображаемые программой просмотра после
запроса всей таблицы employees базы данных Oracle

"<lue«yOaUi -
: Host jdbhostl apljhuedu . D0Name;|PTE Orhw: <^ OmtiB Г Sybase .'

Usemame: jtiaii password: |«*»«

Qyery: [SELECT • FROM employees WHERE salary > 80000

jj BKowResiSs^t
1
Ouety Results - -
ID j FIR STKAME j LASTMAItE j LAMOUAGE 1 SAU
6 Justin Time compiler Java 98000

7 Sir Viet Java 114750

8 Jay Espy Java 128500

Рис. 22.3. Данные, отображаемые QueryViewer после запроса


части таблицы employees базы данных Oracle
22.5. Интерактивная программа просмотра запросов 1041

OueryData "-- ;-

Host |clbhost2 apl.jhu edu OBNeme: |б05741 Onver: r <Jracle (^ Sybase

Usemstrm: ^lai'l Fesswofd:

Queiy: JSELECT ' FROM fruits

if ShowRdsytts l l

Quejy Results
qwafter 1 apples | ajpplesalet j oraitges j of9ii$es3te« 1 tojpseHer \
1 32248 3547 28 :18459 313803 Maria i

2 35009 3850 99 ;i8722 3182 74 Bob [

3 39393 4333 23 18999 3229 83 Joe ' 1

4 42001 4620 11 19333 3286 61 Maria ']

Рис. 22.4, Данные, отображаемые QueryViewer после запроса всей


таблицы fruits базы данных Sybase

II I I I l l l l l l l l l l l I — |||||||"";["f^f|
<Ju«ryD«ta
Host |dDhost2apljhu edu 00 Name J605741 Oilvef:^ Oracia c^ Sybase

Usemarm jhall Paeavyord: |»******

Query: IsELECT apples. oranges FFROM fruits WHERE topseller-: 'Maria'

1stwwRaa5tsn|

. Query RdsuH^
i7ti:r-7fnriTftTtri-iirriifniMmiHitii
apples fMr«ng««
32248 18459 и
1 11
42001 19333

Рис.22.5. Данные, отображаемые программой просмотра после


запроса части таблицы fruits базы данных Sybase

Код QueryViewer
Написать программу, окна которой представлены на рис. 22.1-22.5, относительно
несложно. Если использовать средства класса D a t a b a s e U t i l i t i e s , рассмотренного
выше в этой главе, то объем кода, необходимого для взаимодействия с базой данных,
окажется даже меньше, чем объем кода, реализующего пользовательский интерфейс.
Исходный текст класса QueryViewer приведен в листинге 22.14, здесь же мы вкратце
опишем действия, выполняющиеся после щелчка на кнопке Show Results.
Сначала программа извлекает из интерфейсных элементов сведения об узле, пор­
те и типе драйвера, а также имя базы данных, имя пользователя и пароль. После этого
QueryViewer передает запрос базе и сохраняет результаты.
DBResults results =
DatabaseUtilities.getQueryResults(driver, url,
username, password,
query, true);
1042 Глава 2 2 . JDBC

Затем программа передает результаты модели таблицы (листинг 22.15). Если вы


незнакомы с библиотекой Swing, заметьте, что модель таблицы служит связующим
звеном между элементом J T a b l e и реальными данными.
DBResultsTableModel model = new DBResultsTableModel(results);
JTable table = new JTable(model);
И, наконец, программа отображает JTable в нижней части JFrame и вызывает
метод раек, чтобы изменить размеры окна JFrame.

Листинг 22.14. QueryViewer. Java

package cwp;

import java.awt.*;
import Java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

/•• Интерактивная программа просмотра запроса. Она


* устанавливает соединение с базой данных Oracle или
* Sybase database, выполняет запрос и представляет
* результаты как JTable.
V
public class QueryViewer extends JFrame
implements ActionListener{
public static void main(String[] args) {
new QueryViewer0 ;
}
private JTextField hostField, dbNameField,
queryField, usernameField;
private JRadioButton oracleButton, sybaseButton;
private JPasswordField passwordField;
private JButton showResultsButton;
private Container contentPane;
private JPanel tablePanel;

public QueryViewer () {
super("Database Query Viewer");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
contentPane = getContentPane();
contentPane.add(makeControlPanel(), BorderLayout.NORTH);
packO ;
setVisible(true);
}
/** Если активизируется кнопка Show Results либо если
* пользователь нажимает клавишу <Enter> в момент, когда
* поле редактирования имеет фокус ввода, осуществляется
* обращение к базе данных, результаты помещаются в
* JTable и размеры окна изменяются с тем, чтобы в нем
* могла поместиться таблица.
22.5. Интерактивная программа просмотра запросов 1043

public void actionPerformed(ActionEvent event) {


String host = hostField.getText();
String dbName = dbNameField.getText();
String username = usernameField.getText();
String password =
String.valueOf(passwordField.getPassword());
String query = queryField.getText();
int vendor;
if (oracleButton.isSelected()) {
vendor = DriverUtilities.ORACLE;
} else {
vendor = DriverUtilities.SYBASE;
}
if (tablePanel != null) {
contentPane.remove(tablePanel);
}
tablePanel = makeTcuDlePanel(host, dbName, vendor,
username, password,
query);
contentPane.add(tcdDlePanel, BorderLayout.CENTER);
pack();
}
// Выполнение запроса и включение результатов в компонент
// JTable, который, в свою очередь, содержится в JPanel.

private JPanel makeTablePanel(String host.


String dbName,
int vendor.
String username.
String password.
String query) {
String driver = DriverUtilities.getDriver(vendor);
String url = DriverUtilities.makeURL(host, dbName, vendor);
DBResults results =
DatcdDaseUtilities.getQueryResults(driver, url,
username, password,
query, true);
JPanel panel = new JPanel(new BorderLayout());
if (results == null) {
panel.add(makeErrorLabel() ) ;
return(panel);
}
DBResultsTableModel model =
new DBResultsTableModel(results);
JTable table = new JTadDle (model) ;
table.setFont(new Font("Serif", Font.PLAIN, 17));
table.setRowHeight(28);
JTableHeader header = table.getTableHeader();
header.setFont(new Font("SansSerif", Font.BOLD, 13));
panel.add(table, BorderLayout.CENTER);
panel.add(header, BorderLayout.NORTH);
panel.setBorder
1044 Глава 2 2 . JDBC

(BorderFactory.createTitledBorder("Query Results"));
return(panel);

// Панель, содержащая поля редактирования, флажки


// опций и кнопки.

private JPanel makeControlPanel() {


JPanel panel = new JPanel(new GridLayout(0, 1));
panel.add(makeHostPanel());
panel.add(makeUsernamePanel());
panel.add(makeQueryPanel());
panel.add(makeButtonPanel());
panel.setBorder
(BorderFactory.createTitledBorder("Query Data"));
return(panel);
}
// Панель, содержащая поля редактирования DB Name и
// переключатели, предназначенные для выбора драйвера.
// Размещается в составе управляющей панели.

private JPanel makeHostPanel() {


JPanel panel = new JPanel();
panel.add(new JLabel("Host:"));
hostField = new JTextField(15);
panel.add(hostField);
panel.add(new JLabel(" DB Name:"));
dbNameField = new JTextField(15);
panel.add(dbNameField);
panel.add(new JLabel(" Driver:"));
ButtonGroup vendorGroup = new ButtonGroup();
oracleButton = new JRadioButton("Oracle", true);
vendorGroup.add(oracleButton) ;
panel.add(oracleButton);
sybaseButton = new JRadioButton("Sybase");
vendorGroup.add(sybaseButton);
panel.add(sybaseButton);
return(panel) ;
}
// Панель, содержащая поля редактирования для ввода имени
// пользователя и пароля. Размещается в составе
// управляющей панели.

private JPanel makeUsernamePanel() {


JPanel panel = new JPanel();
usernameField = new JTextField(10);
passwordField = new JPasswordField(10);
panel.add(new JLabel("Username: " ) ) ;
panel.add(usernameField);
panel.add(new JLabel(" Password:"));
panel.add(passwordField) ;
return(panel) ;
}
22.5. Интерактивная программа просмотра запросов 1045

// Панель, содержащая поля редактирования для ввода запросов.


// Размещается в составе управляющей панели.

private JPanel makeQueryPanel() {


JPanel panel = new JPanel();
queryField = new JTextField(40);
queryField.addActionListener(this) ;
panel.add(new JLabel("Query:"));
panel.add(queryField);
return(panel);

// Панель, содержащая кнопку Show Results.


// Размещается в составе управляющей панели.

private JPanel makeButtonPanel() {


JPanel panel = new JPanel();
showResultsButton = new JButton("Show Results");
showResultsButton.addActionListener(this);
panel.add(showResultsButton);
return(panel);
}
// Предупреждение о недопустимом запросе.

private JLabel makeErrorLabel() {


JLabel label = new JLabel("No Results", JLabel.CENTER)
label.setFont(new Font("Serif", Font.BOLD, 36));
return(label);
}

Листинг22.15. DBResultsTableModel. Java

package cwp;
import javax.swing.table.^;

/** Простой класс^ сообщающий JTable о том, как извлечь


* данные из объекта DBResults (который хранит данные,
* полученные из базы).

public class DBResultsTableModel extends AbstractTableModel {


private DBResults results;

public DBResultsTableModel(DBResults results) {


this.results = results;
}
public int getRowCount() {
return(results.getRowCount());
}
1046 Глава 22. JDBC

public int getColumnCount() {


return(results.getColumnCount());
}
public String getColumnName(int column) {
return(results.getColumnNames()[column]),

public Object getValueAt(int row, int column)


return(results.getRow(row)[column]);
}
}

22.6. Заранее подготовленные выражения


Если вам необходимо многократно выполнять одинаковые SQL-запросы, целесо­
образно вместо обычных SQL-запросов воспользоваться заранее подготовленными
(заранее скомпилированными) выражениями. В этом случае выражения в стандарт­
ном формате, допускающие указание параметров, заранее компилируются перед ис­
пользованием. Знаки вопросов в составе выражения заменяются конкретными значе­
ниями. При каждом использовании заранее подготовленного выражения надо заме­
нить некоторые параметры, используя вызов setXxx (например, s e t I n t ,
s e t S t r i n g ) . Заметьте, что номер параметра начинается с единицы. После этого вы­
зывается метод executeQuery (для получения объекта R e s u l t S e t ) либо e x e c u t e /
e x e c u t e U p d a t e (для обеспечения необходимых побочных эффектов) так же, как это
делается при использовании нормальных выражений. Например, если вы хотите по­
высить оклад всем сотрудникам, записи о которых хранятся в базе данных
employees, вы должны использовать код наподобие следующего:
Connection connection =
D r i v e r M a n a g e r . g e t C o n n e c t i o n ( u r l , u s e r , password);
String template =
"UPDATE employees SET s a l a r y = ? WHERE i d = ?";
PreparedStatement statement =
connection.preparestatement(template);
f l o a t [] newSalaries = getNewSalaries ( ) ;
i n t [ ] employeelDs = g e t l D s O ;
f o r ( i n t i=0; i<employeeIDs.length; i++) {
statement.setFloat(l, newSalaries[i]);
statement.setint(2, employeelDs[i]);
statement.execute();
}
Выигрыш от использования заранее скомпилированных выражений существенно
зависит от того, насколько эффективно такие выражения поддерживаются сервером,
и от того, как драйвер обрабатывает обычные запросы. Например, в листинге 22.16
представлен класс, который передает базе данных 40 различных запросов, используя
при этом заранее подготовленные выражения, а затем повторяет те же 40 запросов с
помощью обычных выражений. На персональном компьютере, который подключен к
Internet через модем 28,8 К и взаимодействует с базой данных Oracle, выполнение за­
ранее подготовленных выражений занимает приблизительно в два раза меньше вре­
мени по сравнению с обработкой обычных запросов. В частности, 40 запросов, в ко-
22.6. Заранее подготовленные выражения 1047

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


17,5 секунд, в то время как обработка 40 обычных запросов заняла 35 секунд. Когда
обращения к базе данных Oracle производились через локальную сеть с высокой про­
пускной способностью, время обработки заранее подготовленных выражений со­
ставляло около 70 процентов времени обработки обычных запросов (0,22 секунд для
заранее подготовленных выражений и 0,31 секунд для обычных запросов). При рабо­
те с драйвером Sybase производительность не зависела от типа выражений ни при со­
единении через модем, ни при использовании локальной сети. Для того чтобы оце­
нить производительность для вашей конфигурации системы, скопируйте файл
D r i v e r U t i l i t i e s . J a v a с сервера h t t p : / / w w w . c o r e w e b p r o g r a m m i n g . c o m / , до­
бавьте информацию о драйверах и запустите программу P r e p a r e d S t a t e m e n t s .

Листинг 2 2 . 1 6 . P r e p a r e d S t a t e m e n t s . j a v a

p a c k a g e cwp;

import java.sql.*;

/** Пример, позволяющий с р а в н и т ь р а з н и ц у во времени между


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

public class PreparedStatements {


public static void main(String[] args) {
if (args.length < 5) {
printUsage();
return;

String vendorName = args[4];


int vendor = DriverUtilities.getVendor(vendorName);
if (vendor == DriverUtilities.UNKNOWN) {
printUsage();
return;
}
String driver = DriverUtilities.getDriver(vendor);
String host = args[0];
String dbName = args[l];
String url =
DriverUtilities.makeURL(host, dbName, vendor);
String username = args[2];
String password = args[3];
// Параметр print позволяет убедиться, что программа
// работает корректно,
boolean print = false;
if ((args.length > 5) && (args[5].equals("print"))) {
print = true;
}
Connection connection =
getConnection(driver, url, username, password);
1048 Глава 22. JDBC

if (connection != null) {
doPreparedStatements(connection, print);
doRawQueries(connection, print);
}
}

private static void doPreparedStatements(Connection conn,


boolean print) {
try {
String queryFormat =
"SELECT lastname FROM employees WHERE salary > ?";
PreparedStatement statement =
conn.preparestatement(queryFormat);
long startTime = System.currentTimeMillis();
for(int i=0; i<40; i++) {
statement. setFloatd, i*5000) ;
ResultSet results = statement.executeQuery();
if (print) {
showResults(results);
}
}
long stopTime = System.currentTimeMillis();
double elapsedTime = (stopTime - startTime)/1000.0;
System.out.println("Executing prepared statement " +
"40 times took " +
elapsedTime + " seconds.");
} catch(SQLException sqle) {
System.out.println("Error executing statement: " + sqle);
}
}
public static void doRawQueries(Connection conn,
boolean print) {
try {
String queryFormat =
"SELECT lastname FROM employees WHERE salary > ";
Statement statement = conn.createStatement();
long StartTime = System.currentTimeMillis();
for(int i=0; i<40; i++) {
ResultSet results =
statement.executeQuery(queryFormat + (i*5000));
if (print) {
showResults(results);
}
}
long StopTime = System.currentTimeMillis();
double elapsedTime = (stopTime - startTime)/1000.0;
System.out.println("Executing raw query " +
"40 times took " +
elapsedTime + " seconds.");
} catch(SQLException sqle) {
System.out.println("Error executing query: " -ь sqle);
}
}
private static void showResults(ResultSet results)
22.6. Заранее подготовленные выражения 1049

throws SQLException {
while(results.next О ) {
System.out.print(results.getString(1) + " " ) ;
}
System.out.println();
}

private static Connection getConnection(String driver,


String url,
String username,
String password) {
try {
Class.forName(driver);
Connection connection =
DriverManager.getConnection(url, username, password);
return(connection);
} catch(ClassNotFoundException cnfe) {
System.err.println("Error loading driver: " + cnfe);
return(null);
} catch(SQLException sqle) {
System.err.println("Error connecting: " + sqle);
return(null);
}
}
private static void printUsageO {
System.out.println("Usage: PreparedStatements host " +
"dbName username password " +
"oracle I Sybase [print].");
}
}

22.7. Резюме
JDBC обеспечивает стандартные средства взаимодействия программ, написанных
на Java, с реляционными базами данных. При этом нет необходимости в использова­
нии кода, зависящего от производителя базы, что упрощает процесс работы с не­
сколькими базами данных или переход от одной базы к другой.
Несмотря на то что JDBC стандартизует механизм взаимодействия с базами дан­
ных и структуру данных, получаемых в составе ответа, попытки стандартизовать SQL-
синтаксис не предпринимаются. Это значит, что в составе запросов должны присут­
ствовать SQL-команды, специфические для базы данных конкретного производителя.
SQL-запросы создаются в виде строк символов; специальные методы, предназначен­
ные для этой цели, отсутствуют.
Средства JDBC могут использоваться приложениями и аплетами. При этом следует
помнить, что возможности аплета по установке соединений ограничены узлом, с кото­
рого этот аплет был загружен. Чаще всего JDBC-взаимодействие выполняют приложе­
ния, работающие на стороне сервера, в частности сервлеты и JSP. На стороне сервера
основная задача решается системами управления базами данных; сервлеты и JSP выпол­
няют роль программ промежуточного уровня, обеспечивая базе передачу информации,
переданной броузером, и форматирование результатов, полученных от базы.
ОБРАБОТКА
XML-ДОКУМЕНТОВ

В этой главе...

• Обработка XML-документа посредством Document


Object Model (DOM) Level 2.

• Использование DOM для отображения структуры


XML-документа с помощью JTree.

• Обработка событий, генерируемых при XML-разборе,


посредством Simple АР! for XML Parsing (SAX) 2.0.

• Отображение структуры XML-документа с помощью SAX.

• Подсчет заказов с помощью SAX.

• Преобразование ХМL-документов средствами XSLT.

• Вызов XSLT с помощью пользовательского


дескриптора JSP.

• Устранение зависимости от производителя путем


использования Java API for XML Processing (JAXP).
Sly\ZJ^^

ML представляет собой "метаязык" разметки, предназначенный для описания

X структур данных. Этот язык очень быстро приобрел большую популярность и в


настоящее время используется для создания конфигурационных файлов, обме­
на данными, поддержки В2В-транзакций и решения других задач, возникающих при
разработке корпоративных систем. XML даже применяется для выполнения удален­
ных запросов посредством протокола SOAP (Simple Object Access Protocol — простой
протокол доступа к объектам).
XML-документы удобочитаемы, синтаксический разбор их не вызывает затрудне­
ний, они расширяемы и обрабатываются различными системами. Механизм DTD
(Document Туре Definition — определение типа документа) позволяет задавать грам­
матические правила, которым должны подчиняться документы, предназначенные для
конкретных приложений. Главным преимуществом XML является тот факт, что дан­
ные, представленные в виде XML-документа, совместимы с разными приложениями.
Для данных XML — то же, что и язык Java для программ.

Java = переносимые программы


XML = переносимые данные
в данной главе не уделяется внимания написанию XML-документов; в ней пойдет
речь о разборе этих документов средствами Java. Мы покажем вам, как использовать
Java для обработки XML-кода посредством DOM (Document Object Model — объектная
модель документа), SAX (Simple API for XML— простой API для XML) и XSLT
(Extensible Style sheet Language for Transformations — расширяемый язык стилей для
преобразования).

С п е ц и ф и к а ц и я X M L LО
http://www.w3.org/TR/REC-xml

Web-страница S u n , п о с в я щ е н н а я XML и Java


http://Java.sun.com/xml/
1052 Глава 23. Обработка XML-документов

И н ф о р м а ц и я WWW C o n s o r t i u m о б XML
http://www.w3.org/XML/

A p a c h e X M L Project
http://xml.apache.org/

Ресурсы, посвященные XML


http://xml.coverpages.org/

O'Reilly X M L R e s o u r c e C e n t e r
http://www.xml.com/

2 3 . 1 . Разбор XML-доку мента посредством


DOM Level 2
DOM (Document Object Model) представляет XML-документ в виде древовидной
структуры, которая достаточно просто обрабатывается Java-программой. Преимуще­
ствами DOM является удобство в использовании и возможность модификации струк­
тур данных. К недостаткам можно отнести тот факт, что программа должна хранить
весь документ и полностью выполнять его разбор даже в том случае, когда реально
необходима лишь часть этого документа. В разделе 23.3 описан альтернативный под­
ход, который позволяет выделять небольшой фрагмент XML-кода.

Инсталляция и настройка
DOM не является стандартным компонентом ни Java 2 Standard Edition, ни API
севлетов и JSP. Поэтому, чтобы обеспечить работу с DOM, необходимо скопировать
требуемые классы и настроить их. Для этого надо выполнить следующие действия.
1. Скопировать DOM-совместимые средства разбора. П р и этом вы получаете
Java-классы, совместимые с DOM Level 2 API. Список доступных средств разбо­
ра находится по адресу h t t p : / / w w w . x m l . c o m / p u b / r g / J a v a _ P a r s e r s . В дан­
ной книге мы используем для этой цели Apache Xerces-J. В комплекте с данным
средством разбора поставляется полное описание DOM API в формате Javadoc.
2. Скопировать Java API для обработки XML (JAXP). Данный API реализует
"поверх" DOM специальный слой, который позволяет работать со средствами
разбора различных производителей, не внося изменений в код программы.
Указанный API находится по адресу h t t p : / / J a v a . s u n . c o m / x m l / .
3. Включить информацию о классах DOM в состав переменной окруясения
CLASS PATH. Работая с Apache Xerces, надо предусмотреть в CLASS PATH запись
x e r c e s _ i n s t a l l _ d i r \ x e r c e s . j a r . Например, для приложений в системе
Windows соответствующая установка CLASS PATH будет иметь такой вид:
set CLASSPATH=xerces_install_dir\xerces.jar;%CLASSPATH%
2 3 . 1 . Разбор XML-документа посредством DOM Level 2 1053

Если вы собираетесь использовать DOM в сервлетах и JSP, вам следует скопи­


ровать соответствующий JAR-файл в каталог l i b сервера, распаковать JAR-
файл (используя для этого команду j аг -xvf) в каталог c l a s s e s сервера либо
явным образом изменить переменную окружения сервера CLASS PATH; обычно
это выполняется в сценарии запуска сервера.
4. Включить в CLASSPATH сведения о классах JAXP. Эти классы содержатся в
архиве i a x p i n s t a l l d i r / j a x p . j a r . Например, при работе с Unix/Linux и
оболочкой С s h e l l необходимо использовать следующие установки:
s e t e n v CLASSPATH j a x p _ i n s t a l l _ d i r / j a x p . j a r : $ C L A S S P A T H
Для сервлетов и JSP надо сделать то же, что и на предыдущем этапе.
5. Обеспечить доступ к документации по DOM Level 2 и JAXP API. Официаль­
ная спецификация DOM находится по адресу http://www.w3.org/TR/D0M-
L e v e l - 2 - C o r e / , однако использовать API в формате Javadoc гораздо удобнее.
Такая документация поставляется в составе Apache Xerces и включает инфор­
мацию о JAXP и SAX (см. раздел 23.3).
6. Вывести на печать спецификацию JAXP. Эту спецификацию можно найти по
адресу h t t p : / / J a v a . s u n . c o m / x m l / j a x p - l _ l ~ s p e c . p d f .

Разбор
Для того чтобы выполнить обработку документа средствами DOM, надо решить
две основные задачи: преобразовать XML-текст в структуру данных DOM и найти в
этой структуре интересующую вас информацию. Ниже описаны основные действия,
необходимые для решения данных задач.
1. Сообщить системе, какие средства разбора вы намереваетесь использо­
вать. Это можно сделать различными способами: с помощью системного свой­
ства p a r s e r s . D o c u m e n t B u i l d e r F a c t o r y , посредством j r e _ d i r / l i b / j a x p .
p r o p e r t i e s , используя J2EE Sei"vices API и класс, заданный в МЕТА-INF/
s e r v i c e s / j a v a x . x m l . p a r s e r s . DocumentBuilderFactory. Кроме того, можно
использовать средства разбора, принятые по умолчанию для конкретной сис­
темы. Проще всего воспользоваться DocumentBuilderFactory. Ниже приве­
ден фрагмент кода, который позволяет пользователю указать средства разбора
в командной строке с помощью опции -D. Если данная опция отсутствует, ис­
пользуется Apache Xerces.
public static void main(String[] args) {
String jaxpPropertyName =
"j avax.xml.parsers.DocumentBuilderFactory" ;
if {System.getProperty{jaxpPropertyName) == null) {
String apacheXercesPropertyValue =
"org.apache.xerces.jaxp.DocumentBuilderFactorylmpl";
System.setProperty(jaxpPropertyName,
apacheXercesPropertyValue);
}
1054 Глава 23. Обработка XML-документов

}
2. Создать JAXP-объект для построения документов. О н представляет собой
оболочку для средств разбора XML.
DocumentBuilderFactory builderFactory =
DocumentBuilderFactory.newlnstance();
DocumentBuilder b u i l d e r =
builderFactory.newDocumentBuilder();
Заметьте, что для работы с пространством имен можно использовать методы
s e t N a m e s p a c e A w a r e и s e t V a l i d a t i n g класса D o c u m e n t B u i l d e r F a c t o r y .
3. Вызвать средство разбора для создания объекта Document, представляю­
щего XML-документ. Средство разбора вызывается посредством метода p a r s e
объекта создания документа. Этому методу передается входной поток, URI
(представленный в виде строки) или объект o r g . x m l . s a x . I n p u t S o u r c e .
Класс Document содержит результаты разбора, о ф о р м л е н н ы е в виде древовид­
ной структуры.
Document document = builder.parse(somelnputStream);
4. Нормализовать дерево. Нормализация подразумевает объединение узлов для
нескольких строк и удаление пустых узлов.
document.getDocumentElement().normalize();
5. Получить корневой узел дерева. Корневой узел возвращается как объект
E l e m e n t , являющийся подклассом более общего класса Node, представляюще­
го XML-элемент.
Element rootElement = document.getDocumentElement ();
6. Проверить свойства узла. К этим свойствам относятся имя элемента
(getNodeName), тип узла ( g e t N o d e T y p e ; возвращаемые значения сравниваются с
константами, определенными в классе Node), значение узла ( g e t N o d e V a l u e ; для
текстовых узлов значение представляет собой строку символов, содержащуюся
между открывающим и закрывающим дескрипторами элемента), атрибуты, при-
сутствутощие в открывающем дескрипторе ( g e t At t r i b u t e s ) , и дочерние узлы
( g e t C h i l d N o d e s ; элементы, содержащиеся между открывающим и закрываю­
щим дескрипторами тек}тцего элемента). Каждый из дочерних узлов можно про­
верить рекурсивно.
7. Модифицировать свойства узлов. Вместо извлечения данных из XML-доку­
мента можно модифицировать документ, добавляя ( a p p e n d C h i l d ) или удаляя
( r e m o v e C h i l d ) дочерние узлы либо изменяя значение узла ( s e t N o d e V a l u e ) .
К сожалению, DOM не предоставляет стандартных методов вывода DOM-струк­
туры в текстовом формате. Поэтому вы должны либо сделать это самостоя­
тельно (вывести символ "<", имя узла, имена и значения атрибутов, разделен­
ные знаком равенства, поместив значения атрибутов в кавычки, и завершить
вывод символом ">"), либо воспользоваться одним из существующих пакетов,
генерирующих текстовое представление DOM-элемента.
2 3 . 2 . Пример использования DOM... 1055

23.2. Пример использования DOM:


представление ХМL-документа
с помощью JTree
В листинге 23.1 содержится код класса XMLTree, который отображает структуру
XML-документа с помощью объекта J T r e e . Каждый элемент представляется как узел
дерева и обозначается либо именем элемента, либо именем, за которым следует спи­
сок атрибутов в скобках. Данный класс выполняет следующие действия.
1. Разбирает и нормализует XML-документ, после чего получает корневой эле­
мент. Действия, выполняемые на этом этапе, соответствуют пп. 1-5 последова­
тельности, описанной в предыдущем разделе.
2. Преобразует корневой элемент в узел J T r e e . Если XML-элемент содержит ат­
рибуты (которые могут быть получены посредством вызова метода n o d e .
g e t A t t r i b u t e s ) , узел представляется строкой, состоящей из имени элемента
(getNodeName), за которым следует список атрибутов и их значений, поме­
щенный в скобки. Если атрибуты отсутствуют (метод g e t L e n g t h , применен­
ный к результатам вызова n o d e . g e t A t t r i b u t e s , возвращает значение 0), то
для обозначения узла используется только имя элемента.
3. С помощью g e t C h i l d N o d e s ищет элементы, дочерние по отношению к теку­
щему, преобразует их в узлы J T r e e и связывает эти узлы с родительскими узла­
ми дерева.
4. Действия, соответствующие предыдущему этапу, рекурсивно выполняются над
остальными дочерними элементами.
В листинге 23.2 показан класс, который создает описанный выше объект J T r e e и
помещает его в состав J F r a m e . Как средства разбора, так и XML-документ задаются
пользователем при вызове программы.
Java -Djavax.xml.parsers.DocumentBuilderFactory=xxx XMLFrame
Если опция -D пропущена, применяется Apache Xerces. XML-документ также задается в
командной строке, если же он не указан, для интерактивного выбора файла используется
компонент J F i l e C h o o s e r . Благодаря использованию класса E x t e n s i o n F i l e F i l t e r ,
приведенного в листинге 23.3, файлы, отображаемые в окне элемента J F i l e C h o o s e r , ог^
раничиваются расширениями xml и t I d (библиотека дескрипторов JSP).
На рис. 23.1 показано окно выбора, в котором выделен файл p e r e n n i a l s . xml
(листинг 23.4; DTD-описание приведено в листинге 23.5). На рис. 23.2 и 23.3 показаны
результаты в неразвернутом и частично развернутом виде.
Поскольку в XML-файле содержится ссылка на DTD-описание, Xerces-J предпри­
нимает попытку разбора DTD, даже если проверка документа не предусмотрена. Если
файл p e r e n n i a l s . d t d недоступен, его надо поместить в подкаталог d t d s каталога,
в котором содержится XMLTree, и изменить декларацию DOCTYPE в файле
p e r e n n i a l s . xml следующим образом:
<!DOCTYPE perennials SYSTEM "dtds/perennials.dtd">
1056 Глава 23. Обработка XML-документов

ШЕШШШШШШШШШ шшшшшшшшшшшш^
Lookin. j iCWP-Code
jii ^ ^ Й1 Jil®
лЯ\ cwp-taglib.tid
|i*) orders.xml

iifi] teslxml

fi^mme: jperenmalsx-mi Opt-n 1

Fileo oftyp? y'Mi • • .


zi ^^^^
'^^ 1

Рис. 23. / . Для интерактивного выбора XML-


файла применяется объект J F i l e C h o o s e r ,
использующий E x t e n s i o n F i l e F i l t e r

^E;\CWP2\XML-Code\CWP-Code\p€
S perennials
* ._„] ciaylily(status=in-stock)
шщ ^E:\CWP2\XML-Code\CWP-Code\perennials.xmliililO

-
•^ perennials
„>4 daylily (status=in-stock)
+ 5 daylily (status=in-stock) * cultlvar
+ ..J daylily (status=sold-out) •*•' ,„ J award
+ I daylily (status=in-stock) - _J award
> ™i daylily (status=limited) # name (note=snnall-flowered)
» year
+ I award
« bloom (code=M)
* cost(currency^US, dlscount=3)
+ ; d ay 11 ly (statu s=i n- sto с k)
- ^ daylily (status=sold-ou^
* cultivar
% award
* name
« year
» bloom (code=E)
* cost(currency=US)
• < daylily (status=in-stock)
+ ™J daylily (status=limlted)

Рис. 23.2. JTree-представление корневого Рис. 23.3. JTree-представление документа


узла документа p e r e n n i a l s .xml (листинг perennials .xml, в котором некоторые
23.4) и его дочерних элементов узлы развернуты

Листинг 23.1.XMLTree.java

import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;
import java.io.*;
import org.w3c.dom.*;
import javax.xml.parsers.^;
/** Данный класс генерирует объект JTree, который
* представляет структуру XML-документа, содержащегося
* в указанном файле либо полученного через заданный
* входной поток. Для разбора используется DOM.
V
public class XMLTree extends JTree {
public XMLTree(String filename) throws lOException {
this(filename, new FileInputStream(new File(filename)))
23.2. Пример использования DOM... 1057

}
public XMLTree(String filename, InputStream in) {
super(makeRootNode(in));
}
// Данный метод должен быть объявлен как static, поскольку
/ / о н вызывается при обращении к родительскому конструктору
// (super). В этот момент объект еще на создан.
private static DefaultMutableTreeNode
makeRootNode(InputStream in) {
try {
// Благодаря использованию JAXP DocumentBuilderFactory
// код не зависит от конкретных средств разбора DOM.
// Для указания используется системное свойство
// javax.xml.parsers.DocumentBuilderFactory
// (устанавливается либо из Java-программы, либо
// посредством опции -D) или
// jre_dir/lib/jaxp.properties.
DocumentBuilderFactory builderFactory =
DocvimentBuilderFactory. newlnstance () ;
DocumentBuilder builder =
builderFactory.newDocumentBuilder();
// Стандартный DOM-код. Метод parse запускает процесс
// разбора и возвращает полностью обработанный объект
// Document. Рекурсивно обходя дерево, мы копируем
// узлы (не текстовые) в узлы JTree.
Document document = builder.parse(in);
document.getDocumentElement().normalize();
Element rootElement = document.getDocumentElementO;
DefaultMutableTreeNode rootTreeNode =
buildTree(rootElement);
return(rootTreeNode) ;
} catch(Exception e) {
String errorMessage =
"Error making root node: " + e;
System.err.println(errorMessage);
e.printStackTrace();
return(new DefaultMutableTreeNode(errorMessage));
}
}
private static DefaultMutableTreeNode
buildTree(Element rootElement) {
// Для корневого узла создается узел JTree, после чего
// для каждого дочернего узла создается узел JTree и
// добавляется к корневому узлу. Метод addChildren
// вызывается рекурсивно.
DefaultMutableTreeNode rootTreeNode =
new DefaultMutableTreeNode(treeNodeLabel(rootElement)) ;
addChildren(rootTreeNode, rootElement);
return(rootTreeNode);
}
private static void addChildren
(DefaultMutableTreeNode parentTreeNode,
1058 Глава 23. Обработка ХМЬ-документов

Node parentXMLElement) {
// Данный метод вызывается рекурсивно, находит дочерние
// элементы и присоединяет их к родительскому узлу.
/ / В данном случае используются два типа узлов: узлы,
// отражающие структуру XML-документа, и узлы,
// соответствующие графическому представлению JTree.
// Для узлов, соответствующих графическому представлению
// Jtree, будем использовать в составе имен переменных in the
// слово "tree". Таким образом, "childElement" - дочерний
// XML-элемент, а "childTreeNode" - JTree-элемент.
// Данный метод копирует узлы, отличные от текста и
// комментариев, из структуры XML в структуру JTree.

NodeList childElements =
parentXMLElement.getChildNodes();
for(int i=0; i<childElements.getLength(); i++) {
Node childElement = childElements.item(i);
if (!(childElement instanceof Text ||
childElement instanceof Comment)) {
DefaultMutableTreeNode childTreeNode =
new DefaultMutableTreeNode
(treeNodeLabel(childElement));
parentTreeNode.add(childTreeNode);
addChildren(childTreeNode, childElement);
}
}
}
// Если XML-элемент не содержит атрибутов, узел JTree
// получает имя XML-элемента. При наличии атрибутов
/ / и х имена и значения указываются в скобках после имени
// XML-элемента.

private static String treeNodeLabel(Node childElement) {


NamedNodeMap elementAttributes =
childElement.getAttributes0;
String treeNodeLabel = childElement.getNodeName();
if (elementAttributes != null &&
elementAttributes.getLength() > 0) {
treeNodeLabel = treeNodeLabel + " (";
int numAttributes = elementAttributes.getLength();
for(int i=0; i<numAttributes; i++) {
Node attribute = elementAttributes.item(i);
if (i > 0) {
treeNodeLabel = treeNodeLabel + ", ";
}
treeNodeLabel =
treeNodeLabel + attribute.getNodeName() +
"=" + attribute.getNodeValue0;
}
treeNodeLabel = treeNodeLabel + " ) " ;
}
return(treeNodeLabel);
}
}
23.2. Пример использования DOM... 1059

Листинг 23.2. XMLFrame .Java

import java.awt.*;
import javax.swing.*;
import java.io.*;

/** Данная программа вызывает средства разбора для


* ХМЪ~документа и отображает документ в виде компонента
* JTree. Как средства разбора, так и документ задаются
* пользователем. Средства разбора указываются при
* вызове программы:
* Java -Djavax.xml.parsers.DocumentBuilderFactory=xxx XMLFrame
* Если средства разбора не заданы, применяется Apache Xerces.
* XML задается в командной строке; если же при вызове
* программы он не указан, компонент JFileChooser используется
* для интерактивного выбора файла.

public class XMLFrame extends JFrame {


public static void main(String[] args) {
String jaxpPropertyName =
" javax. xml. parsers. DociimentBuilderFactory" ;

// Фабрика для создания объекта разбора передается


// посредством опции -D. По умолчанию испольдуются
// средства раэбора Apache.
if (System.getProperty(jaxpPropertyName) == null) {
String apacheXercesPropertyValue =
"org.apache.xerces.jeixp.DocumentBuilderFactorylmpl";
System.setProperty(jaxpPropertyName,
apacheXercesPropertyValue);
}
String filename;
if (args.length > 0) {
filename = args[0];
} else {
String[] extensions = { "xml", "tld" };
WindowUtilities.setNativeLookAndFeel();
filename = ExtensionFileFilter.getFileName(".",
"XML Files",
extensions);
if (filename == null) {
filename = "test.xml";
}
}
new XMLFrame(filename);

public XMLFrame(String filename) {


try {
WindowUtilities.setNativeLookAndFeel();
JTree tree = new XMLTree(filename);
JFrame frame = new JFrame(filename);
frame.addWindowListener(new ExitListener()
Container content = frame.getContentPane() .
1060 Глава 2 3 . Обработка XML-документов

content.add(new JScrollPane(tree));
frame.раск() ;
frame.setVisible(true);
} catch(lOException ioe) {
System.out.println("Error creating tree: " + ioe)
}

Листинг 23.3. ExtensionFilePilter. Java

import Java.io.File;
import java.util.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;

/•• FileFilter позволяет указывать расширения файлов,


* которые должны отображаться. Статический метод getFileName
* используется для вывода компонента JFileChooser.
* <Р>
* Аналогичное решение используется в демонстрационных
* материалах Sun SwingSet.
*/

public class ExtensionFileFilter extends FileFilter {


public static final int LOAD = 0;
public static final int SAVE = 1;
private String description;
private boolean allowDirectories;
private Hashtable extensionsTable = new HashtableO;
private boolean allowAll = false;

public ExtensionFileFilter(boolean allowDirectories) {


this.allowDirectories = allowDirectories;
}
public ExtensionFileFilter0 {
this(true);
}

public static String getFileName(String initialDirectory,


String description,
String extension) {
String[] extensions = new String[]{ extension };
return(getFileName(initialDirectory, description,
extensions, LOAD));
}

public static String getFileName(String initialDirectory,


String description.
String extension,
int mode) {
String[] extensions = new String[]{ extension }•
23.2. Пример использования DOM... 1061

return(getFileName(initialDirectory, description,
extensions, mode));
}
public static String getFileName(String initialDirectory,
String description.
String[] extensions) {
return(getFileName(initialDirectory, description,
extensions, LOAD));
}

/•• Активизирует компонент JFileChooser, который отображает


* список файлов с заданными расширениями. В режиме SAVE
* диалоговое окно содержит кнопку Save; в противном
* случае в окне присутствует кнопка Open. Метод возвращает
* объект String, содержащий путь к файлу. В случае, если
* активизирована кнопка Cancel, возвращается значение null.
V
public static String getFileName(String initialDirectory,
String description,
String[] extensions,
int mode) {
ExtensionFileFilter filter = new ExtensionFileFilter();
filter.setDescription(description);
for(int i=0; i<extensions.length; i++) {
String extension = extensions[i];
filter.addExtension(extension, true);
}
JFileChooser chooser =
new JFileChooser(initialDirectory);
chooser.setFileFilter(filter);
int selectVal = (mode==SAVE) ? chooser.showSaveDialog(null)
: chooser.showOpenDialog(null);
if (selectVal == JFileChooser.APPROVE_OPTION) {
String path = chooser.getSelectedFile().getAbsolutePath();
return(path);
} else {
JOptionPane.showMessageDialog(null, "No file selected.");
return(null);
}
}
public void addExtension(String extension,
boolean caselnsensitive) {
if (caselnsensitive) {
extension = extension.toLowerCase();
}
if (!extensionsTable.containsKey(extension)) {
extensionsTable.put(extension,
new Boolean(caselnsensitive));
if (extension.equals("*") ||
extension.equals("*.*") I I
extension.equals(".*")) {
allowAll = true;
}
}
1062 Глава 23. Обработка XML-доку ментов

public boolean accept(File file) {


if (file.isDirectory()) {
return(allowDirectories);
}
if (allowAll) {
return(true);
}
String name = file.getName ();
int dotlndex = name.lastlndexOf('.');
if ((dotlndex == -1) || (dotlndex == name.length() - 1)) {
return(false);
}
String extension = name.substring(dotlndex + 1 ) ;
if (extensionsTable.containsKey(extension)) {
return(true);
}
Enumeration keys = extensionsTable.keys();
while(keys.hasMoreElements0) {
String possibleExtension = (String)keys.nextElement();
Boolean caseFlag =
(Boolean)extensionsTable.get(possibleExtension);
if ((caseFlag != null) &&
(caseFlag.equals(Boolean.FALSE)) &&
(possibleExtension.equalsIgnoreCase(extension))) {
return(true);
}
return(false)
}
public void setDescription(String description)
this.description = description;
}
public String getDescription() {
return(description);
}

Листинг 23.4. perennials.xml

<?xml version="1.0" ?>


<!DOCTYPE perennials SYSTEM
"http://archive.corewebprogramming.com/dtds/perennials.dtd">
<perennials>
<daylily status="in-stock">
<cultivar>Luxury Lace</cultivar>
<award>
<name>Stout Medal</name>
<year>1965</year>
</award>
<award>
<name note="small-flowered">Annie T. Giles</name>
23.2. Пример использования DOM... 1063

<year>1965</year>
</award>
<award>
<name>Lenington All-American</name>
<year>1970</year>
</award>
<bloom code="M">Midseason</bloom>
<cost discount="3" currency="US">ll.75</cost>
</daylily>
<daylily status="in-stock">
<cultivar>Green Flutter</cultivar>
<award>
<name>Stout Medal</naine>
<year>197 6</year>
</award>
<award>
<name note="small-flowered">Annie T. Giles</name>
<year>1970</year>
</award>
<bloom code="M">Midseason</blooiTi>
<cost discount="3+" currency="US">7.50</cost>
</daylily>
<daylily status="sold-out">
<cultivar>My Belle</cultivar>
<award>
<name>Stout Medal</name>
<year>1984</year>
</award>
<bloom code="E">Early</bloom>
<cost currency="US">12.00</cost>
</daylily>
<daylily status="in-stock">
<cultivar>Stella De Oro</cultivar>
<award>
<name>Stout Medal</name>
<year>1985</year>
</award>
<award>
<name note="miniature">Donn Fishcer Memorial Cup</name>
<year>197 9</year>
</award>
<bloom code="E-L">Early to Late</bloom>
<cost discount="10+" currency="US">5.00</cost>
</daylily>
<daylily status="limited">
<cultivar>Brocaded Gown</cultivar>
<award>
<name>Stout Medal</name>
<year>198 9</year>
</award>
<bloom code="E">Early</bloom>
<cost currency="US" discount="3+">14.50</cost>
</daylily>
</perennials>
1064 Глава 23. Обработка XML-доку ментов

Листинг 23.5. p e r e n n i a l s . d t d

<?хш1 v e r s i o n = " 1 . 0 " encocling="ISO-8859-l" ?>


<!ELEMENT perennials (daylily)*>

<!ELEMENT daylily (cultivar, award*, bloom, cost)+>


<!ATTLIST daylily
status (in-stock I limited | sold-out) #REQUIRED>

<!ELEMENT cultivar (#PCDATA)>

<!ELEMENT award (name, year)>

<!ELEMENT name (#PCDATA)>


<!ATTLIST name
note CDATA #IMPLIED>

<!ELEMENT year (#PCDATA)>

<!ELEMENT bloom (#PCDATA)>


<!ATTLIST bloom
code (E I EM I M I ML I L I E-L) #REQUIRED>

<!ELEMENT cost (#PCDATA)>


<!ATTLIST cost
discount CDATA #IMPLIED>
<!ATTLIST cost
currency (US i UK | CAN) "US">

23.3. Разбор XML-документов посредством


SAX 2.0
Использовать DOM для обработки XML-документов очень удобно, поскольку клас­
сы DOM обеспечивают реальный разбор текста; вам остается лишь найти среди ре­
зультатов разбора требуемые данные. Однако разбор с помощью DOM занимает дос­
таточно много времени, и если вас интересует лишь небольшой фрагмент документа,
такой подход оказывается неоправданным. Предположим, например, что вам нужно
одно слово из XML-документа, представляющего большой словарь. При работе с
DOM пришлось бы хранить и разбирать весь словарь, что заняло бы значительный
объем ресурсов. Используя SAX, вам надо сохранять лишь интересующие вас фраг­
менты, а разбор можно прекратить в любой удобный для вас момент. С другой сторо­
ны, чтобы организовать разбор посредством SAX, приходится прилагать дополни­
тельные усилия. Система оповещает о событиях, связанных с разбором, например о
появлении открывающего дескриптора <language r a t i n g = " g o o d " > и закрывающе­
го дескриптора </language>, либо об обнаружении содержимого элемента (нап­
ример, слова Java, находящегося между указанными выше открывающим и закрываю­
щим дескрипторами). Вам необходимо решить, какие действия должны выполняться
23.3. Разбор XML-документов посредством SAX 2.0 1065

при возникновении подобных событий. Знаком ли вам подобный подход? Обработка


XML посредством SAX несколько напоминает определение библиотек пользователь­
ских дескрипторов (см. раздел 20.7).

Инсталляция и настройка
SAX не является стандартным компонентом ни Java 2 Standard Edition, ни API
сервлетов и JSP. Поэтому, чтобы обеспечить работу с SAX, необходимо скопировать
требуемые классы и настроить их для использования в вашей программе, т.е. выпол­
нить следующие действия.

1. Скопировать SAX-совместимые средства разбора. П р и этом вы получаете


Java-классы, совместимые с SAX 2 API. Список доступных средств разбора нахо­
дится по адресу h t t p : / / w w w . x m l . c o m / p u b / r g / J a v a _ P a r s e r s . В данной кни­
ге мы используем для этой цели Apache Xerces-J. В комплекте с указанным сред­
ством разбора поставляется полное описание SAX API в формате Javadoc.
2. Скопировать Java SAX цдя обработки XML (JAXP). Данный API реализует
слой "поверх" SAX, к о т о р ы й позволяет работать со средствами разбора раз­
личных производителей, не внося изменений в код программы. Указанный API
находится по адресу h t t p : / / J a v a , s u n . c o m / x m l / .
3. Включение информации о классах SAX в состав переменной окружения
CLASSPATH. Работая с Apache Xerces, надо предусмотреть в CLASS PATH запись
x e r c e s i n s t a l l d i r X x e r c e s . j a r . Например, для приложений в системе
Windows соответствующая установка CLASSPATH будет иметь вид
set CLASSPATH=xerces_install_dir\xerces.jar;%CLASSPATH%
Если вы собираетесь использовать SAX в сервлетах и JSP, вам следует скопиро­
вать соответствующий JAR-файл в каталог l i b сервера, распаковать JAR-файл
(используя для этого команду j a r - x v f ) в каталог c l a s s e s либо явным обра­
зом изменить переменную окружения сервера CLASSPATH; обычно это выпол­
няется в сценарии запуска сервера.
4. Включить в CLASSPATH сведения о классах JAXP. Эти классы содержатся в
архиве j a x p i n s t a l l d i r / j a x p . j a r . Например, при работе U N I X / L i n u x и
оболочкой С shell необходимо использовать следующие установки:
s e t e n v CLASSPATH jaxp_install_dir/jaxp.jar:$CLASSPATH
Для сервлетов и JSP надо поступить так же, как и на предыдущем этапе.
5. Обеспечить доступ к документации по SAX 2 и JAXP API. Вы можете найти
документацию по API по а д р е с у h t t p : / / w w w . m e g g i n s o n , c o m / S A X / J a v a /
j a v a d o c / , однако API в составе Apache Xerces использовать гораздо удобнее.
Дополнительная и н ф о р м а ц и я по SAX находится по адресу h t t p : / / w w w .
megginson.com/SAX/.
1066 Глава 2 3 . Обработка XML-документов

Разбор
Чтобы выполнить обработку документа средствами SAX, надо решить две основ­
ные задачи: создать объект поддержки содержимого и вызвать средств разбора для
этого объекта. Ниже описаны основные действия, необходимые для решения данных
задач.
1. Сообщить системе, какие средства разбора вы намереваетесь использовать.
Это можно сделать различными способами: с помощью системного свойства
j a v a x . x m l . p a r s e r s . SAX-ParserFactory, посредством j r e _ d i r / l i b / j a x p .
p r o p e r t i e s , используя J2EE Services API и класс, заданный в МЕТА-INF/
s e r v i c e s / j a v a x . x m l . p a r s e r s . SAX-ParserFactory. Кроме того, можно ис­
пользовать средства разбора, принятые по умолчанию для конкретной системы.
Ниже приведен фрагмент кода, который позволяет пользователю указать средст­
ва разбора в командной строке с помощью опции -D. Если данная опция отсутст­
вует, используется Apache Xerces.
public static void main(String[] args) {
String jaxpPropertyName =
"javax.xml.parsers.SAXParserFactory";
if (System.getProperty(jaxpPropertyName) == null) {
String apacheXercesPropertyValue =
"org.apache.xerces.j axp.SAXParserFactorylmpl";
System.setProperty(jaxpPropertyName^
apacheXercesPropertyValue);
}

}
2. Создать экземпляр объекта для разбора документов. Для этого надо сначала
создать фабрику объектов, а затем сам объект разбора.
SAXParserFactory f a c t o r y = S A X P a r s e r F a c t o r y . n e w l n s t a n c e ( ) ;
SAXParser p a r s e r = f a c t o r y . n e w S A X P a r s e r ( ) ;
Заметьте, что для работы с пространством имен можно использовать методы
setNamespaceAware и s e t V a l i d a t i n g класса SAXParserFactory.
3. Создать объект поддержки содержимого для обработки событий разбора.
Данный объект поддержки обычно представляет собой подкласс класса
D e f a u l t Handler; в нем надо переопределить некоторые из описанных ниже
методов.
• startDocument, endDocument
Эти методы переопределяются для того, чтобы выполнять требуемые дейст­
вия в начале и в конце разбора документа. Они вызываются без параметров.
• startElement, endElement
23.4. Пример использования SAX: вывод общих сведений... 1067

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


дескриптора элемента. Методу s t a r t E l e m e n t передаются четыре парамет­
ра: URI пространства имен (объект S t r i n g ; если пространство имен отсут­
ствует, используется пустая строка), пространство имен или префикс
(объект S t r i n g ; если пространство имен отсутствует— пустая строка),
полностью определенное имя элемента (объект S t r i n g , например
p r e f i x r m a i n N a m e , или, если пространство имен отсутствует, mainName) и
объект A t t r i b u t e s , представляющий атрибуты, указанные в открывающем
дескрипторе. Методу e n d E l e m e n t передаются те же параметры, за исклю­
чением атрибутов.
• characters, ignoreableWhitespace
Эти методы используются для обработки содержимого документа. Каждому
из них передаются три параметра: символьный массив, начальный и конеч­
ный индекс. Как правило, часть массива преобразуется в объект S t r i n g , для
чего т р и указанных параметра передаются конструктору объекта.
4. Обращение к объекту разбора с указанием объекта поддержки содержимо­
го. П р и обращении вызывается метод p a r s e объекта разбора, которому пере­
дается входной поток, URI, представленный в виде строки, или o r g . x m l .
s a x . I n p u t - S o u r c e , a также объект поддержки содержимого.
parser.parse(filename, handler);

23.4. Пример использования SAX: вывод


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

• startElement
Данный метод выводит сообщение о том, что найден открывающий дескрип­
тор элемента. Атрибуты, связанные с этим элементом, отображаются в скобках.
Этот метод начинает отображение данных выводом пробелов. Число пробелов
определяется переменной i n d e n t a t i o n . Перед завершением работы данный
метод увеличивает значение i n d e n t a t i o n на 2 (в начале работы программы
значение этой переменной устанавливается равным 0).
• endElement
Данный метод уменьшает значение переменной i n d e n t a t i o n на 2, после чего
выводит сообщение о том, что встретился закрывающий дескриптор элемента.
• characters
Метод c h a r a c t e r s отображает первое слово в теле элемента; уровень отступа,
определяемый переменной i n d e n t a t i o n , остается неизменным.
1068 Глава 23. Обработка XML-документов

В листинге 23.8 показан код программы, которая позволяет пользователю выбрать


SAX-совместимые средства разбора и XML-файл, а затем вызывает объект разбора,
передавая ему объект поддержки содержимого (см. листинг 23.7). Н а рис. 23.4 показа­
но окно выбора файла, а в листинге 23.6 представлены результаты обработки файла
orders . xml (см. листинг 23.9).

ШЕЕОВНВШВН! ШШШШШШШШЩ
Lookr»: , J„jCWP-Code
"3 ^ ^ j ^ i g p l
.:?^Yt«^r?i^
Ilia) cwp-tagllb-Md

| i « | perennials.xml
1 *il test.xml

fmmme: [orders xmi Open 1

fl^of^C |XML Files '^i l^iw^ 1 1

Рис. 23.4. Выбор файла orders . xml

Листинг 23.6. Часть выходных данных, полученных в процессе разбора файла


orders. xml посредством SAXPrinter

Start tag: orders


Start tag: order
Start tag: count
37
End tag: count
Start tag: price
49.99
End tag: price
Start tag: book
Start tag: isbn
0130897930
End tag: isbn
Start tag: title
Core...
End tag: title
Start tag: authors
Start tag: author
Marty...
End tag: author
Start tag: author
Larry...
End tag: author
End tag: authors
End tag: book
End tag: order
Start tag: order
Start tag: count
1
End tag: count
Start tag: price
23.4. Пример использования SAX: вывод общих сведений... 1069

9.95
End tag: price
Start tag: yacht
Start tag: manufacturer
Luxury...
End tag: manufacturer
Start tag: model
M-1
End tag: model
Start tag: standardFeatures (oars=plastic, lifeVests=none)
false
End tag: standardFeatures
End tag: yacht
End tag: order
... (Остальные результаты пропущены)
End tag: orders

Листинг 23.7. PrintHandleroava

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.util.StringTokenizer;
/** Класс поддержки содержимого, который выводит сведения
* об открывающем и закрывающем дескрипторах, а также
* первое слово в теле элемента. Для каждого вложенного
* элемента отступ увеличивается на два пробела.

public class PrintHandler extends DefaultHandler {


private int indentation = 0;
/** Если встречается открывающий дескриптор, то он выводится,
* а отступ увеличивается на два пробела. Если элемент
* содержит атрибуты, они отображаются в скобках
* после имени элемента.
*/
public void startElement(String namespaceUri,
String localName,
String qualifiedName,
Attributes attributes)
throws SAXException {
indent(indentation);
System.out.print("Start tag: " + qualifiedName);
int numAttributes = attributes.getLength();
// Для дескриптора <someTag> выводится "someTag".
// Для <someTag attl="Vall" att2="Val2">, выводится
// "someTag (attl=Vall, att2=Val2).
if (numAttributes > 0) {
System.out.print(" (");
for(int i=0; i<numAttributes; i++) {
if (i>0) {
1070 Глава 23. Обработка XML-документов

System.out.print(", " ) ;
}
System.out.print(attributes.getQName(i) + "=" +
attributes.getValue(i));
}
System.out.print(")");
}
System.out.println();
indentation = indentation + 2;

/** Если встречается закрывающий дескриптор, то он выводится,


* а отступ уменьшается на два пробела.
*/

public void endElement(String namespaceUri,


String localName,
String qualifiedName)
throws SAXException {
indentation = indentation - 2;
indent(indentation);
System.out.println("End tag: " + qualifiedName);

/** Вывод первого слова тела элемента. */

public void characters(char[] chars,


int startlndex,
int endlndex) {
String data = new String(chars, startlndex, endlndex);
/ / П о умолчанию ограничителем для StringTokenizer
// считается пробел.
StringTokenizer tok = new StringTokenizer(data);
if (tok.hasMoreTokens0) {
indent(indentation);
System.out.print(tok.nextToken());
if (tok.hasMoreTokens0) {
System.out.println("...");
} else {
System.out.println0;
}
}
}

private void indent(int indentation) {


for(int i=0; i<indentation; i++) {
System.out.print (" " ) ;
}
}
}
23.4. Пример использования SAX: вывод общих сведений... 1071

Листинг 23.8.SAXPrinter.Java
import j a v a x . x m l . p a r s e r s . * ;
import o r g . x m l . s a x . * ;
import org.xml.sax.helpers.*;
/** Программа, использующая SAX для вывода данных об
* открывающих и закрывающих дескрипторах в ХМЬ-файле,
* а также первых слов в теле элемента.

public class SAXPrinter {


public static void main(String[] args) {
String jaxpPropertyName =
"javax.xml.parsers.SAXParserFactory";
// Фабрика объектов разбора задается в командной строке
// с помощью 01ЩИИ -D. Если данная опция отсутствует,
// используются средства разбора Apache,
if (System.getProperty(jaxpPropertyName) == null) {
String apacheXercesPropertyValue =
"org.apache.xerces.jaxp.SAXParserFactoryImpl";
System.setProperty(j axpPropertyName,
apacheXercesPropertyValue);
}
String filename;
if (args.length > 0) {
filename = args[0];
} else {
String!] extensions = { "xml", "tld" };
WindowUtilities.setNativeLookAndFeel();
filename = ExtensionFileFilter.getFileName(".",
"XML Files",
extensions);
if (filename == null) {
filename = "test.xml";
}
}
printOutline(filename);
System.exit(0);
}
public static void printOutline(String filename) {
DefaultHandler handler = new PrintHandler();
SAXParserFactory factory = SAXParserFactory.newlnstance<)
try {
SAXParser parser = factory.newSAXParser();
parser.parse(filename, handler);
} catch(Exception e) {
String errorMessage =
"Error parsing " + filename + ": " + e;
System.err.println(errorMessage);
e.printStackTrace ();
}
}
1072 Глава 2 3 . Обработка XML-документов

Листинг 23.9. orders .xml

<?xml version="1.0" ?>


<orders>
<order>
<count>37</count>
<price>49.99</price>
<book>
<isbn>0130897 930</isbn>
<title>Core Web Programming Second Edition</title>
<authors>
<author>Marty Hall</author>
<author>Larry Brown</author>
</authors>
</book>
</order>
<order>
<count>l</count>
<price>9.95</price>
<yacht>
<manufacturer>Luxury Yachts, Inc.</manufacturer>
<mode 1 >M-К/mode 1 >
<standardFeatures oars^^'plastic"
lifeVests="none">
false
</standardFeatures>
</yacht>
</order>
<order>
<count>3</count>
<price>22.22</price>
<book>
<isbn>B000059Z4H</isbn>
<title>Harry Potter and the Order of the Phoenix</title>
<authors>
<author>J.K. Rowling</author>
</authors>
</book>
</order>
<order>
<count>2</count>
<price>104 52689.01</price>
<yacht>
<manufacturer>We В Boats, Inc.</manufacturer>
<model>236-A</model>
<standardFeatures bowlingAlley="double"
tennisCourt="grass">
true
</standardFeatures>
</yacht>
</order>
<order>
<count>l3</count>
<price>49.99</price>
<book>
23.5. Пример использования SAX: подсчет заказов на книги 1073

<isbn>0130897 930</isbn>
<title>Core Web Programming Second Edition</title>
<authors>
<author>Marty Hall</author>
<author>Larry Brown</author>
</authors>
</book>
</order>
</orders>

23.5. Пример использования SAX: подсчет


заказов на книги
Одним из преимуществ SAX по сравнению с DOM является тот факт, что при ра­
боте с SAX не обязательно обрабатывать и хранить весь документ; части текста, не
интересующие вас, можно пропустить. Представленный ниже пример выполняет по­
иск разделов XML-файла, которые выглядят приблизительно следующим образом:
<orders>
<count>23</count>
<book>
<isbn>0130897 930</isbn>

</book>

</orders>
Программа подсчитывает, сколько экземпляров книги с указанным ISBN содер­
жится в составе набора заказов. Таким образом, большинство элементов в файле не
представляет никакого интереса. Поскольку в SAX, в отличие от DOM, не предусмот­
рено автоматическое хранение данных, необходимо специально позаботиться о том,
чтобы требуемая информация была записана. При этом возникают трудности, свя­
занные с тем, что элемент i s b n располагается после элемента count. Поэтому значе­
ние count надо временно сохранять, но учитывать его в общем объеме заказов следу­
ет только в том случае, если значение i s b n совпадает с ISBN интересующей книги.
Для того чтобы данная задача могла быть выполнена, в объекте поддержки содержи­
мого переопределены следующие четыре метода.
• startElement
Данный метод проверяет, совпадает ли имя элемента со значением count или
i s b n . Если совпадение обнаружено, то устанавливается соответствующий флаг,
который сообщает методу c h a r a c t e r s о том, что содержимое элемента долж­
но быть обработано.
• endElement
Метод endElement проверяет, совпадает ли имя элемента со значением count
или i s b n . Если совпадение обнаружено, сбрасывается флаг, установленный в
методе s t a r t E l e m e n t .
1074 Глава 23. Обработка XML-доку ментов

• characters
Если установлен флаг c o u n t , данный метод разбирает значение элемента, пре­
образует его в тип i n t и сохраняет до получения данных об элементе i s b n . Ес­
ли установлен флаг i s b n , метод читает информацию в теле дескриптора и
сравнивает ее с требуемым значением ISBN. П р и положительном результате
сравнения временно сохраненное содержимое c o u n t добавляется к значению
основного счетчика, учитывающего общее количество заказов на книгу.
• endDocument
Данный метод выводит значение основного счетчика. Если заказано менее 250
экземпляров книги, программа советует сделать в будущем больше заказов.
Юхасс C o u n t B o o k s (листинг 23.11) применяет к XML-файлу средство разбора, ука­
занное пользователем. В качестве объекта поддержки содержимого используется
C o u n t H a n d l e r . На рис. 23.5 показано окно выбора файла, а на рис. 23.6 — результаты
обработки файла o r d e r s . xml (см. листинг 23.9).

Is^iifW.!
Look In: jljCWP-Code "3 ©j _gj d^ s pm
_л] cwp-taglib.tid

л\ perennialsj<ml
«3 test.xml ^Message
Y o u Ofder^Kl ^ ?OJ>l^«; "HH

FBe name: |ordersj<mP


Ф Core We& Vm^titmm bntmu ЬШ^п,
Ивв$е тйт m&m next %m«^

FHesoftyp6t |xML Files -T]

Рис. 23.5. Пользователь выбрал файл Рис. 23.6. Результаты обрабо­


orders.xml тки файла orders.xml прог­
раммой CountBooks

Листинг 23.1 о. CountHandler * Java

import org.xml.sax.^;
import org.xml.sax.helpers.*;
import java.util.StringTokenizer;
import javax.swing.*;

/** Класс поддержки содержимого, предназначенный для подсчета


* числа заказов. Учитывается информация, содержащаяся в
* элементах следующего вида:
* <ХМР>

* <count>23</count>
* <book>
* <isbn>01308 97 930</isbn>

</book>
</ХМР>
Прочая информация, содержащаяся в документе,
23.5. Пример использования SAX: подсчет заказов на книги 1075

* не учитывается.
V
public class CountHandler extends DefaultHandler {
private boolean collectCount = false;
private boolean collectlSBN = falser-
private int currentCount = 0;
private int totalCount = 0;

/** При обнаружении открывающего дескриптора элемента


* count или isbn устанавливается флаг, с тем чтобы
* метод characters мог обработать содержимое дескриптора.
V
public void startElement(String namespaceUri,
String localName,
String qualifiedName,
Attributes attributes)
throws SAXException {
if (qualifiedName.equals("count")) {
collectCount = true;
currentCount = 0;
} else if (qualifiedName.equals("isbn")) {
collectlSBN = true;
}
}

/** При обнаружении открывающего дескриптора элемента


* count или isbn флаг сбрасывается. В результате метод
* characters не будет обрабатывать тело элемента.
V
public void endElement(String namespaceUri,
String localName,
String qualifiedName)
throws SAXException {
if (qualifiedName.equals("count")) {
collectCount = false;
} else if (qualifiedName.equals("isbn")) {
collectlSBN = false;
}

/** Поскольку элемент count предшествует элементу book


* (в состав которого входит элемент isbn), необходимо
* организовать временное хранение информации, содержащейся
* в count. Впоследствии, если содержимое элемента
* isbn совпадет с ISBN книги, информация, находящаяся
* в элементе count, добавляется к основному счетчику.

public void characters(char[] chars,


int startlndex,
int endlndex) {
if (collectCount || collectlSBN) {
String dataString =
1076 Глава 23. Обработка XML-доку ментов

new String(chars, startlndex, endlndex).trimO;


if (collectCount) {
try {
currentCount = Integer.parseint(dataString);
} catch(NumberFormatException nfe) {
System.err.println("Ignoring malformed count:
dataString);
}
} else if (collectlSBN) {
if (dataString.equals("0130897930")) {
totalCount = totalCount + currentCount;
}
}
}
}

/** Вывод информации о числе заказов.


*/

public void endDocument() throws SAXException {


String message =
"You ordered " + totalCount + " copies of \n" +
"Core Web Programming Second Edition.\n";
if (totalCount < 250) {
message = message + "Please order more next time!'
} else {
message = message + "Thanks for your order.";
}
JOptionPane.showMessageDialog(null, message);
}
}

Листинг 23.11.CountBooks.Java

import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;

/** Программа подсчета числа заказов. Учитывается


* информация, содержащаяся в элементах следующего вида:

* <ХМР>
* ...
* <count>23</count>
* <book>
* <isbn>0130897 930</isbn>

* </book>

* </ХМР>
* Прочая информация в составе документа игнорируется.
23.6. Преобразование XML посредством XSLT 1077

public class CountBooks {


public static void main(String[] args) {
String jsixpPropertyName =
" j avaix. xml. parsers. SAXParserFactory" ;
// Фабрика объектов раэбора задается в командной строке
// с помощью опции -*D. Если данная опция отсутствует,
// используются средства разбора Apache,
if (System.getProperty(jeixpPropertyName) == null) {
String apacheXercesPropertyValue =
"org.apache.xerces.jaxp.SAXParserFactorylmpl";
System.setProperty(j axpPropertyName,
apacheXercesPropertyValue);
}
String filename;
if (args.length > 0) {
filename = args[0];
} else {
String[] extensions = { "xml" };
WindowUtilities.setNativeLookAndFeel();
filename = ExtensionFileFilter.getFileName(".",
"XML Files",
extensions);
if (filename == null) {
filename = "orders.xml";
}
}
countBooks(filename);
System.exit(0);
}
private static void countBooks(String filename) {
DefaultHandler handler = new CountHandler();
SAXParserFactory factory = SAXParserFactory.newlnstance() ,
try {
SAXParser parser = factory.newSAXParser();
parser.parse(filename, handler);
} catch(Exception e) {
String errorMessage =
"Error parsing " + filename + ": " + e;
System.err.printIn(errorMessage);
e.printStackTrace();
}
}

23.6. Преобразование XML посредством XSLT


XSLT— это язык, позволяющий преобразовывать XML-файлы в HTML и другие
типы документов. Выполняя преобразование, XSLT-процессор конвертирует XML-
документ в соответствии с адресами Xpath и правилами форматирования, заданны­
ми посредством листов стилей XML (XSL). Информация XPath идентифицирует раз­
личные части XML-документа, а листы стилей определяют формат выходных данных.
1078 Глава 23. Обработка XML-документов

Преимущество XSLT состоит в том, что для преобразования одного XML-доку­


мента можно определять различные листы стилей. Например, если база данных воз­
вращает ответ на запрос в формате XML, то в зависимости от протокола, используе­
мого клиентом (HTTP или WAP), сервлет может применять различные стили для
конвертирования данных соответственно в форматы HTML или WML. В качестве
другого примера можно привести использование XSLT в электронной коммерции.
Получив заказ на изделие, система может посредством разных листов стилей преоб­
разовать заказ в два различных документа, относящихся к оплате и доставке заказа.
Спецификации XSLT, XSL и XPath поддерживает WWW Consortium. Найти соот­
ветствующие документы можно по следующим адресам:

XSLT L 0
http://www.w3.org/TR/xslt.html

XSL LO
http://www.w3.org/TR/xsl/

XPath LO
http://www.w3.org/TR/xpath.html
Кроме того, информацию об XSLT, XSL и XPath можно получить, обратившись к
URL h t t p : / / w w w . w 3 . o r g / S t y l e / X S L / . Узел, поддерживаемый GoXML и содержа­
щий XSLT-ресурсы, расположен по адресу h t t p : //www. x s l t . com/.

Инсталляция и настройка
XSLT не является стандартным компонентом ни Java 2 Standard Edition, ни API
сервлетов и JSP. Поэтому, чтобы обеспечить работу с XSLT, необходимо скопировать
требуемые классы и настроить их для использования в вашей программе, т.е. выпол­
нить следующие действия.
L Скопировать XSLT-совместимые средства преобразования. К ним относятся
Java-классы, соответствующие спецификации XSLT LO. Список средства разбо­
ра XSLT можно получить по адресу h t t p : / / w w w . w 3 . o r g / S t y l e / X S L / или
h t t p : / / w w w . x s l t . c o m / x s l t _ t o o l s _ e n g i n e s . h t m . П р и написании данной
книги использовался продукт Apache Xalan-J ( h t t p : / / x m l . a p a c h e . o r g /
xalan-j /).
2. Учесть в п е р е м е н н о й окрулсения CLASSPATH к л а с с ы DOM и SAX. Работа
средств XSLT базируется на DOM и SAX. П р и использовании Apache Xalan-J на­
до указать в составе CLASSPATH архив x e r c e s . j a r . Конфигурация Apache
Xalan-J была рассмотрена в разделах 23.1 и 23.3. Заметьте, что x e r c e s . j a r
включает инсталляционный каталог Apache Xalan-J.
3. Учесть в п е р е м е н н о й о к р у ж е н и я CLASSPATH к л а с с ы XSLT. П р и работе с
Xalan эти классы находятся в архиве x a l a n _ i n s t a l l _ d i r \ x a l a n . j a r . На­
пример, для Windows следует задать
set CLASSPATH=xalan_install_dir\xalan.jar;
%CLASSPATH%
23.6. Преобразование XML посредством XSLT 1079

Для UNIX/Linux и оболочки С shell данная установка будет иметь следующий вид:
setenv CLASSPATH xalan_install_dir/xalan.jar:
$CLASSPATH
Если вы собираетесь использовать XSLT в сервлетах и JSP, вам следует скопи­
ровать JAR-файлы DOM, SAX и XSLT в каталог l i b сервера, распаковать JAR-
файл (используя для этого команду j a r - x v f ) в каталог c l a s s e s либо изме­
нить переменную окружения сервера CLASSPATH; обычно это выполняется в
сценарии запуска сервера.
4. Обеспечить доступ к спецификациям XSL L0 и XPath L0. Официальные доку­
менты, содержащие данные спецификации, находятся по адресу h t t p : / / w w w .
w3.org/Style/XSL/.
5. Обеспечить доступ к спецификации XSLT. Спецификацию XSLT можно най­
ти по адресу h t t p : / / w w w . w 3 . o r g / T R / x s l t . h t m l . В составе Apache Xalan
спецификация XSLT реализована посредством ТгАХ (Transformation API for
XML— API преобразования для XML). Документация по ТгАХ API в формате
Javadoc доступна по адресу h t t p : / / x m l . a p a c h e . o r g / x a l a n - j / a p i d o c s / .

Преобразование
в процессе XSLT-конвертирования решаются две основные задачи: создание XSL-
шаблона и запуск процедуры преобразования XML-документа. Н и ж е описаны основ­
ные действия, необходимые для выполнения этих задач.
1. Сообщить системе, какое средство разбора доллсно использоваться для
преобразования. Это можно сделать несколькими способами: посредством сис­
темного свойства j a v a x . x m l . t r a n s f o r m . T r a n s f o r m F a c t o r y , с помощью
j r e _ d i r / l i b / j a x p . p r o p e r t i e s , используя J2EE Services API и класс, заданный
в M E T A - I N F / s e r v i c e s / j a v a x . x m l . t r a n s f o r m . T r a n s f o r m F a c t o r y . Кроме то­
го, можно использовать средства обработки, принятые по умолчанию для кон­
кретной системы. Поскольку XSLT зависит от DOM и SAX, необходимо сообщить
системе, какие средства разбора DOM и SAX должны применяться при обработке
документа. Конфигурация средств DOM и SAX была рассмотрена в разделах 23.1
и 23.3. По умолчанию Apache Xalan-J использует Apache Xerces-J.
2. Сформировать фабрику объектов для создания преобразователей. Перед
обработкой XML-документа необходимо создать объект T r a n s f o r m e r F a c t o r y .
О н позволяет создать различные преобразователи для разных шаблонов, за­
данных посредством листов стилей.
TransformerFactory factory =
TransformerFactory.newlnstance();
3. Создать преобразователь для конкретного шаблона. Для всех листов стилей
можно сгенерировать отдельные преобразователи. Каждый преобразователь
применим к различным XML-документам.
S o u r c e x s l = new S t r e a m S o u r c e ( x s l S t r e a m ) ;
Templates template = factory.newTemplates(xsl);
Transformer transformer = template.newTransformer();
1080 Глава 23. Обработка ХМЬ-документов

Как правило, в качестве источника XSL-кода используется объект S t r e a m S o u r c e .


При создании объекта S t r e a m S o u r c e для XSL4j)apuia применяются объекты
F i l e , R e a d e r или I n p u t S t ream, ссылающиеся на этот файл, либо строка, пред­
ставляющая URI.
4. Вызвать преобразователь для обработки исходного документа. Для запуска
процесса преобразования вызывается метод t r a n s f o r m , которому в качестве
параметров передаются объект S t r e a m S o u r c e для исходного документа и объ­
ект R e s u l t , в который помещается преобразованный документ.
S o u r c e xml = new S t r e a m S o u r c e ( x m l S t r e a m ) ;
R e s u l t r e s u l t = new S t r e a m R e s u l t ( o u t p u t S t r e a m ) ;
tranformer.transform(xml, result);
Подобно исходному коду XSL, исходный XML-документ обычно представляется
как S t r e a m S o u r c e , при создании которого используется объект F i l e , R e a d e r ,
I n p u t s t r e a m , либо строка URI. П р и создании S t r e a m R e s u l t для преобразо­
ванного файла также могут применяться F i l e , W r i t e r , O u t p u t S t r e a m или URI.

Листинг 23.12 представляет класс, предназначенный для XSLT-преобразования


документов. Исходный XML- и XSL-код представляют объекты R e a d e r или F i l e , а
для хранения преобразованного документа используются W r i t e r или F i l e . Преиму­
щество представления документов посредством объектов R e a d e r и W r i t e r состоит в
том, что объекты хранятся в памяти. В этом случае исходные документы могут быть
обработаны с помощью S t r i n g R e a d e r или C h a r A r r a y R e a d e r , а результирующие
документы— посредством S t r i n g W r i t e r или C h a r A r r a y W r i t e r . Например, если
сервлет получает ответ на запрос о базе данных в виде XML-документа, он может вы­
полнить XSLT-преобразование полученной информации и представить результаты,
передаваемые клиенту в HTML-формате. П р и этом нет необходимости записывать на
диск ни исходный XML-код, ни преобразованный документ.

Листинг 2 3 . 1 2 . X s l T r a n s f o r m e r . j a v a

p a c k a g e cwp;

import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import java.io.*;
import java.util.*;

/** XSLT-преобразователь для обработки XML-документа.


* Для каждого документа создается новый преобразователь.
* Средства XSLT, DOM и SAX используют системные параметры,
* принимаемые по умолчанию.
V
public class XslTransformer {
private TransformerFactory factory;

public XslTransformer() {
factory = TransformerFactory.newlnstance0;
}
23.6. Преобразование XML посредством XSLT 1081

/** Преобразование XML- и XSL-документов, представленных


* как <code>Reader</code>. Результаты преобразования
* записываются в объект <code>Writer</code>.
* В результате XML-документ можно обрабатывать как
* String (<code>StringReader</code>) и сохранять в памяти,
* а не на диске, что создает дополнительные преимущества.
* Выходной документ также может обрабатываться как
* String (<code>StringWriter</code>) либо как
* <code>JSPWriter</code>.
V
public void process(Reader xmlFile, Reader xslFile,
Writer output)
throws TransformerException {
process(new StreamSource(xmlFile),
new StreamSource(xslFile),
new StrecunResuit (output) ) ;
}

/** Преобразование XML- и XSL-документов, представленных


* как <code>File</code>, результаты преобразования
* записываются в объект <code>Writer</code>.
* Выходной документ может обрабатываться как String
* (<code>StringWriter</code)> либо как
* <code>JSPWriter</code>.
*/

public void process(File xmlFile, File xslFile,


Writer output)
throws TransformerException {
process(new StreamSource(xmlFile),
new StreamSource(xslFile),
new StreamResult(output));
}

/** Преобразование <code>фaйлa</code> XML с использованием


* <code>фaйлa</code> XSL. Результаты преобразования
* помещаются в <code>OutputStream</code>. Результаты могут
* быть представлены как <code>FileOutputStream</code> или
* <code>ByteArrayOutputStream</code>.
V
public void process(File xmlFile, File xslFile,
OutputStream out)
throws TransformerException {
process(new StreamSource(xmlFile),
new StreamSource(xslFile),
new StreamResult(out));
}

/** Преобразование исходного XML-кода средствами XSLT based


* с использованием шаблона, созданного на основе
* XSL-документа. Результаты преобразования представляются
* как объект <code>Result</code>.
V
1082 Глава 23. Обработка XML-доку ментов

public void process(Source xml, Source xsl. Result result)


throws TransformerException {
try {
Templates template = factory .newTemplates (xsl) ;
Transformer transformer = template.newTransformer();
transformer.transform(xml, result);
} catch(TransformerConfigurationException tee) {
throw new TransformerException(
tee.getMessageAndLocation());
} catch (TransformerException te) {
throw new TransformerException(
te.getMessageAndLocation());
}
}

23.7. Пример использования XSLT: редактор


документов
в листинге 23.13 показан простой редактор документов, созданный с использова­
нием средств Swing. На трех вкладках (панелях) в окне редактора отображаются XML-
документ, XSL-файл и результирующий документ, преобразованный средствами
XSLT. Пользователь может редактировать как XML-текст, так и XSL-код, отображаю­
щиеся на вкладках. В каждой из вкладок содержится компонент Document Рапе с воз­
можностью прокрутки, наследующий характеристики J E d i t o r P a n e (см. листинг
23.14). Содержимое XML- и XSL-вкладок отображается как обычный текст, а инфор­
мация на XSLT-вкладке интерпретируется как HTML-документ. После загрузки XML-
и XSL-файлов выбор XSLT-панели приводит к запуску процесса преобразования XML-
документа. П р и этом используются стили, определенные посредством XSL-файла, а
результаты преобразования отображаются как HTML-документ в XSLT-панели.

Л и с т и н г 2 3 . 1 3 . X s l t E x a m p I e . Java

import javax.xml.transform.*;
import java.awt.*;
import Java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.io.*;
import cwp.XslTransformer;

/•k-k Редактор документов, который преобразует XML- и XSL-код


* средствами XSLT и представляет результаты как HTML-документ.
* В окне программы отображаются три вкладки: панель для
* XML-документа с возможностью редактирования, панель для
* XSL-текста (также допускает редактирование) и панель
* для представления результатов в формате HTML. После
* загрузки XML- и XSL-файлов выбор вкладки XSLT приводит
23.7. Пример использования XSLT: редактор документов 1083

* к запуску процедуры преобразования. Если возникают


* проблемы с представлением XML- или XSL-документа,
* отображается окно с соответствующим сообщением.
V
public class XsltExample extends JFrame
implements ChangeListener {
private static final int XML = 0;
private static final int XSL = 1;
private static final int XSLT = 2;
private static final String DEFAULT_TITLE = "XSLT Example";
private static final String[] tabTitles =
{ "XML", "XSL", "XSLT" };
private static final String[] extensions =
{ "xml", "xsl", "html" };
private Action openAction, saveAction, exitAction;
private JTabbedPane tabbedPane;
private DocumentPane[] documents;
private XslTransformer transformer;

public XsltExample() {
super(DEFAULT_TITLE) ;
transformer = new XslTransformer();
WindowUtilities.setNativeLookAndFeel();
Container content = getContentPane{);
content.setBackground(SystemColor.control);

// Установка меню.
JMenuBar menubar = new JMenuBar();
openAction = new OpenAction()
saveAction = new SaveAction{)
exitAction = new ExitAction()
JMenu fileMenu = new JMenu("File");
fileMenu.add(openAction);
fileMenu.add(saveAction);
fileMenu.add(exitAction);
menubar.add(fileMenu) ;
setJMenuBar(menubar) ;

// Установка вкладок.
tabbedPane = new JTabbedPane();
documents = new DocumentPane[3];
for(int i=0; i<3; i++) {
documents[i] = new DocumentPane();
JPanel panel = new JPanelO;
JScrollPane scrollPane = new JScrollPane(documents[i]);
panel.add(scrollPane) ;
tabbedPane.add(tabTitles[i], scrollPane);
}
documents[XSLT].setContentType(DocumentPane.HTML);
// Компонент JEditorPane содержит ошибку: метод setText
// некорректно распознает HTML-документ, в состав которого
// входит элемент МЕТА, содержащий CONTENT-TYPE.
// Это не происходит, если предварительно посредством setPage
// создается EditorKit. Xalan автоматически добавляет
/ / к документу МЕТА CONTENT-TYPE. Таким образом, необходимо
1084 Глава 23. Обработка XML-документов

// предварительно загружать документ, содержащий


// МЕТА CONTENT-TYPE.

documents[XSLT].loadFile("XSLT-Instructions.html");
documents[XSLT].setEditable(false);
tabbedPane.addChangeListener(this);
content.add(tabbedPane, BorderLayout.CENTER);

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize (450, 350);
setVisible(true);
}

/** Проверяется, какая из панелей выбрана пользователем.


* Если вкладки XML и XSL содержат документы, выбор
* вкладки XSLT запускает процесс преобразования.

public void stateChanged(ChangeEvent event) {


int index = tabbedPane.getSelectedlndex();
switch (index) {
case XSLT: if (documents[XML].isLoadedO &&
documents[XSL].isLoaded()) {
doTransform();
}
case XML:
case XSL: updateMenuAndTitle(index);
break;
default:

/** Получение XML- и XSL-документов в текстовом виде


* (String), передача StringReader и выполнение
* XSLT-преобразования. При генерации исключения
* отображается сообщение, которое описывает проблему.
V
private void doTransform() {
StringWriter strWriter = new StringWriter();
try {
Reader xmlInput =
new StringReader (docглaents [XML] .getTextO) ;
Reader xslInput =
new StringReader(documents[XSL].getText());
transformer = new XslTransformer();
transformer.process(xmlInput, xsllnput, strWriter);
} catch(TransformerException te) {
JOptionPane.showMessageDialog(this,
"Error: " + te.getMessage0);
}
documents[XSLT].setText(strWriter.toString());

/** Обновление заголовка приложения. Заголовок должен


23.7. Пример использования XSLT: редактор документов 1085

* отображать имя файла, который был загружен в


* выбранную панель. Обновление меню (Save, Load)
* в зависимости от выбранной вкладки.
*/
private void updateMenuAndTitle(int index) {
if ((index > -1) && (index < documents.length)) {
saveAction.setEnabled(documents[index].isLoaded()) ;
openAction.setEnabled(documents[index].isEditable());
String title = DEFAULT_TITLE;
String filename = documents[index].getFilename();
if (filename.length 0 > 0) {
title += " - [" + filename + " ] " ;

setTitle(title);
}
}

/•• Отобразить диалоговое окно для загрузки нового файла


* или для сохранения файла, отображающегося на
* выбранной вкладке.
*/

private void updateDocument(int mode) {


int index = tabbedPane.getSelectedlndex();
String description = tabTitles[index] + " Files";
String filename = ExtensionFileFilter.getFileName(".",
description,
extensions[index],
mode);
if (filename != null) {
if (mode==ExtensionFileFilter.SAVE) {
documents[index].saveFile(filename) ;
} else {
documents[index].loadFile(filename);
}
updateMenuAndTitle(index);
}
}
public static void main(String[] args) {
new XsltExample();
}
// Действие меню Open, предназначенное для
// загрузки файла.
class OpenAction extends AbstractAction {
public OpenAction0 {
super("Open . . . " ) ;
}
public void actionPerformed(ActionEvent event) {
updateDocument(ExtensionFileFilter.LOAD);
}
}
// Действие меню Save, предназначенное для сохранения
1086 Глава 23. Обработка XML-доку ментов

// документа, содержащегося в выбранной панели,


class SaveAction extends AbstractAction {
public SaveAction0 {
super("Save");
setEnabled(false);
}
public void actionPerformed(ActionEvent event) {
updateDocument(ExtensionFileFilter.SAVE);
}
}

// Действие меню Exit для завершения приложения,


class ExitAction extends AbstractAction {
public ExitAction0 {
super("Exit");
}
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
}

Листинг 2 3 . 1 4 . DocijmentPane. Java

import java.awt.*;
import Java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.net.*;

/** Компонент JEditorPane, поддерживающий загрузку и


* сохранение документа. Тип документа должен быть
* "text/plain" (по умолчанию) либо "text/html".
V
public class DocumentPane extends JEditorPane {
public static final String TEXT = "text/plain";
public static final String HTML = "text/html";

private boolean loaded = false;


private String filename fi I I ,

/•• Текущая страница, отображаемая в панели, которая


* допускает редактирование.
V
public void setPage(URL url) {
loaded = false;
try {
super.setPage(url);
File file = new File(getPage().toString());
setFilename(file.getName());
loaded = true;
} catch (lOException ioe) {
23.7. Пример использования XSLT: редактор документов 1087

System.err.println("Unable to set page: " + url);

/** Текст на странице замещает существующий документ.

public void setText(String text) {


super.setText(text);
setFilename("");
loaded = true;
}
/** Загрузка файла в панель, допускающую редактирование.

* Заметьте, что метод setPage класса JEditorPane проверяет


* URL текущей страницы и если он совпадает с URL страницы,
* предназначенной для загрузки, данные <Ь>не</Ь>
* загружаются.
V
public void loadFile(String filename) {
try {
File file = new File(filename);
setPage(file.toURL());
} catch (lOException mue) {
System.err.println("Unable to load file: " + filename);
}
}
public void saveFile(String filename) {
try {
File file = new File(filename);
FileWriter writer = new FileWriter(file);
writer.write(getText ());
writer.close ();
setFilename(file.getName0);
} catch (lOException ioe) {
System.err.println("Unable to save file: " + filename);
}
}

/** Получение имени загруженного файла. */

public String getFilename() {


return(filename);
}
/** Указание имени файла, содержащего документ. */

public void setFilename(String filename) {


this.filename = filename;
}
/** Данный метод возвращает значение true, если документ
* загружен посредством <code>setPage</code> либо
1088 Глава 2 3 . Обработка ХМЬ-документов

* <code>setText</code>.

public boolean isLoadedO {


return(loaded);
}
}

Результаты выполнения X s l t E x a m p l e показаны на рис. 23.7-23.9. В частности,


внешний вид панели, которая содержит XML-код, загруженный из файла
p e r e n n i a l s . xml (листинг 23.4), показан на рис. 23.7. Панель с XSL-кодом, загружен­
ным из файла p e r e n n i a l s . x s l (листинг 23.15), показана на рис. 23.8. И, наконец, на
рис. 23.9 представлены результаты преобразования XML-документа. В этом примере
данные о наградах извлекаются из XML-документа (дескриптор d a y l i l y ) и представ­
ляются в формате HTML-таблицы.
Заметьте, что для того, чтобы XSLT-преобразование могло быть выполнено по­
средством Apache Xalan-J, DTD-описание должно быть доступно. Файл p e r e n n i a l s .
d t d находится по адресу h t t p : / / a r c h i v e . c o r e w e b p r o g r a m m i n g . c o m / d t d s / . Если
вы хотите запустить пример на локальной машине, поместите p e r e n n i a l s . d t d в
подкаталог d t d s , находящийся в том каталоге, в котором расположен XML-файл. П р и
этом декларацию DOCTYPE, которая имеет вид
<!DOCTYPE p e r e n n i a l s SYSTEM
"http://archive.corewebprogramming.com/dtds/perennials.dtd">
надо изменить следующим образом:
<!DOCTYPE p e r e n n i a l s SYSTEM " d t d s / p e r e n n i a l s . d t d " >
Если вы включите атрибут d o c t y p e - p u b l i c в состав элемента x s l : o u t p u t , Xalan
разместит в первой строке выходного документа выражение DOCTYPE.
М е т о д и к а профессионалов

Для того чтобы в состав сгенерированного документа было включе­


но выражение DOCTYPE, надо задать атрибут doctype-publlc эле­
мента xsl: output.

Листинг 2 3 . 1 5 . p e r e n n i a l s . x s l

<?xml v e r s i o n = " 1 . 0 " ? >


<xsl:stylesheet version="l.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"
doctype-public ="-//W3C//DTD HTML 4.0 Transitional//EN"/>

<xsl:template match="/">
<HTML>
<HEAD>
<TITLE>Daylilies</TITLE>
</HEAD>
23.7. Пример использования XSLT: редактор документов 1089

<BODY>
<TABLE CELLPADDING^"3">
<CAPTION>Stout Medal Award</CAPTION>
<TR>
<TH>Year</TH>
<TH>Cultivar</TH>
<TH>Bloom Season</TH>
<TH>Cost</TH>
</TR>
< ! — Выбор элементов daylily, содержащих элемент
award, для которого значение элемента name
равно Stout Medal. -->
<xsl:apply-templates
select="/perennials/daylily[award/nanie='Stout Medal*]"/>
<TR>
<TD C0LSPAN="4" ALIGN="CENTER">
E-early M-midseason L-late</TD>
</TR>
</TABLE>
</BODY>
</HTML>
</xsl:template>

<xsl:template match="daylily">
<TR>
<TD><xsl:value-of select="award/year"/></TD>
<TD><xsl:value-of select="cultivar"/></TD>
< ! — Выбор bloom. -->
<TD ALIGN="CENTER"><xsl:value-of select="bloom/@code"/></TD>
<TD ALIGN="RIGHT"><xsl:value-of select="cost"/></TD>
</TR>
</xsl:template>

</xsl:stylesheet>

p x « . T Exampte -||>еге1«Ц«Шр^ ..dSliSi I^XSLT EKampte - {реш*(»Ш9Ш1 JPi xj

:)<ML|XSL|KSLT| XML ^^SLJXSLTI


<'гт1уевюп'="1.0'*?> <?xinlveis»on="l,0'7>
<ID0CTYPE perenruals SYSTEM <xsl stylesheet version-"! .0"
"http (kiciavt corewri}progrananiiig.com/'dtds/peienniaJs.dtd''> anhs xsl-"http://www.wr3.oisfl999«SL/Tmisfonn">
: <pereimials> <xsl:output mBthod="html"
I <daylily status* "m-stock."> doctype-pubhc »"-//W3C//DTD HTML 4.0 Transitional//EN">
<cultiv»r*Luxury L«;«</cultivar>
<xsl:tempkte match-T>
<nan»e>Stout Medal</Mnie> <HTML>
<ye4i>1965<^er> <HEAD>
<fv/nrd> <TITLE^Daylilies<n-ITLE>
<:awani> <^EAD>
<хт» note="sxnall-ik»wered">Aiuue T. Giles<ftiame> <ВООУ>
<3reai»1965</y«4r> <TABLE CELLPADDING-"3">
<feward> <CAPTION>Stout Medal Awird<CAPTION>

^ jfM
Рис. 23.7. Внешний вид вкладки XML, содер­ Рис. 23.8. Внешний вид вкладки XSL,
жащей XML-код, загруженный из файла содержащей XSL-код, загруженный из файла
p e r e n n i a l s .xml (листинг 23.4) p e r e n n i a l s . x s l (листинг23.15)
1090 Глава 23. Обработка XML-документов

|к^Ж-^к^Д!т'1^|ЦШ|Я -lOlxlj
FISe

XML) XSL ><St^l

Stout Medal Award


Year Cultivar Bloom Season Cost
1965 Luxury Lace M 11.75
1976 Green Flutter M 7.50
1984 MyBeUe E 1200
1985 StdlaDeOro E-L 5.00
1989 Brocaded Gown E 1450
E-early M-midseason L-late
Рис. 23.9. Результаты XSLT-преобразования
файлов p e r e n n i a l s . хш1 (листинг 23.4)
и p e r e n n i a l s . x s l (листинг 23.15)

23.8. Пример использования XSLT:


пользовательский JSP-дескриптор
В данном примере при выполнении XSLT-преобразования применяется пользова­
тельский JSP-дескриптор. С его помощью HTML-таблица представляется в формате, ко­
торый зависит от типа броузера. Дело в том, что Netscape 4.7 и более ранние его версии
не поддерживают HTML-элементы THEAD, TBODY и TFOOT, в то же время соответствую­
щие дескрипторы нормально обрабатываются Internet Explorer 4.x (подробное описа­
ние этих элементов приведено в разделе 2.4). Поэтому целесообразно использовать для
обработки XML-файла p e r e n n i a l s .xml (листинг 23.4) различные листы стилей. Кон­
кретный зависит от того, какой из броузеров обратился к JavaServer Page
D a y l i l i e s . j s p (листинг 23.18). Первый из XSL-файлов p e r e n n i a l s - i e . x s l (листинг
23.16) форматирует документ для Internet Explorer, используя при этом элементы
THEAD, TBODY и TFOOT. Второй ХВЕ-файл p e r e n n i a l s - n s . x s l (листинг 23.17) ориен­
тирован на Netscape.
Файл описания библиотеки дескрипторов, x s l t r a n s f o r m . t l d , представлен в
листинге 23.19. Благодаря ему появляется возможность использовать пользователь­
ский дескриптор x s l t r a n s f o r m в JSP D a y l i l i e s . j s p . В роли класса поддержки
пользовательского дескриптора выступает cwp. t a g s . X s l T r a n s f o r m T a g . Для поль­
зовательского дескриптора определены три атрибута: xml (исходный XML-файл;
данный атрибут является обязательным), x s l i e (XSL-файл для Internet Explorer) и
x s l n s (XSL-файл для Netscape; данный атрибут также является обязательным). Файл,
указанный посредством атрибута x s l n s , будет использоваться по умолчанию для лю­
бой клиент-программы кроме Internet Explorer. Дополнительную информацию о
пользовательских дескрипторах см. в разделе 20.7.
Код класса X s l T r a n s f o r m T a g представлен в листинге 23.20. Метод d o S t a r t T a g
создает объекты F i l e для XML- и XSL-документов, причем выбор XSL-файла определя­
ется значением поля заголовка U s e r - A g e n t , содержащегося в составе HTTP-запроса.
После определения исходных файлов XSLT-преобразование выполняется посредством
X s l T r a n s f o r m e r (листинг 23.12), а результаты записываются в J s p W r i t e r .
2 3 . 8 . П р и м е р использования XSLT. 1091

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


расположенных в различных каталогах сервера. На рис. 23.10 показано размещение
этих файлов при использовании сервера Tomcat. Если DTD-описание (файл
p e r e n n i a l s . d t d ) , расположенный по адресу h t t p : / / w w w . c o r e w e b p r o g r a m m i n g .
c o m / d t d s / , недоступен, его надо записать в подкаталог d t d s (см. рис. 23.10) и изме­
нить декларацию DOCTYPE следующим образом:
<!DOCTYPE p e r e n n i a l s SYSTEM "dtds/perennials.dtd">
Результаты обращения к JSP посредством броузера Internet Explorer 5.0, выпол­
няющегося в системе Windows 2000, показаны на рис. 23.11. Результаты для Netscape
4.7, выполняющегося в среде Windows 98, представлены на рис. 23.12.

TOMCAT_HOME/webapps/ROOT/

• cwp/Daylilies.jsp

L cwp-tags/xsltransform.tid

WEB-INF/classes/cwp/perennials.xml
perennials-ie.xml
perennials-ns.xsl
XslTransformer.class

U dtds/perennials.dtd

L tags/XslTransformTag.class

Рис. 23.10. Расположение файлов для сервера Tomcat

Листинг 2 3 . 1 6 . p e r e n n i a l s - i e . x s l

<?xml v e r s i o n = " 1 . 0 " ? >


< ! — Использование элементов THEAD, TBODY и TFOOT. — >
< ! — Код ориентирован на Internet Explorer 4.x и более
поздние версии данного броузера. -->
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="l.0">
<xsl: output method="htrril"/>
<xsl:template match="/">
<TABLE CELLPADDING="3" RULES="GROUPS" ALIGN="CENTER">
<CAPTION>Stout Medal Award</CAPTION>
<COLGROUP>
<COL ALIGN="CENTER"/>
<COL ALIGN="LEFT"/>
1092 Глава 23. Обработка XML-документов

<COL ALIGN="CENTER"/>
</COLGROUP>
<COLGROUP ALIGN="RIGHT"/>
<THEAD>
<TR>
<TH>Year</TH>
<TH>Cultivar</TH>
<TH>Bloom Season</TH>
<TH>Cost</TH>
</TR>
</THEAD>
<TBODY>
< ! — Выбор элементов daylily, содержащих элемент
award, для которого значение элемента name
равно Stout Medal. — >
<xsl:apply-templates
select="/perennials/daylily[award/name= * Stout MedalЧ"/>
</TBODY>
<TFOOT>
<TR>
<TD C0LSPAN="4">E-early M-midseason L-late</TD>
</TR>
</TFOOT>
</TABLE>
</xsl:template>
<xsl:template match="daylily">
<TR>
<TD><xsl:value-of select="award/year"/></TD>
<TD><xsl:value-of select="cultivar"/></TD>
<!-- Выбор bloom. — >
<TD><xsl:value-of select="bloom/@code"/></TD>
<TD><xsl:value-of select="cost"/></TD>
</TR>
</xsl:template>
</xsl:stylesheet>

Листинг 2 3 . 1 7 . p e r e n n i a l s - n s . x s l

<?xml version="l.0"?>
< ! — Использование основных элементов, предназначенных
для создания таблицы. — >
< ! — Данный код ориентирован на броузер Netscape. -->
<xsl:stylesheet version="l.О"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<TABLE CELLPADDING="3" B0RDER="1" ALIGN="CENTER">
<CAPTION>Stout Medal Award</CAPTION>
<TR>
<TH>Year</TH>
<TH>Cultivar</TH>
<TH>Bloom Season</TH>
<TH>Cost</TH>
23.8. Пример использования XSLT... 1093

</TR>
< ! — Выбор элементов daylily, содержащих элемент
award, для которого значение элемента name
равно Stout Medal. — >
<xsl:apply-templates
select="/perennials/daylily[award/name=*Stout Medal']"/>
<TR>
<TD C0LSPAN="4" ALIGN="CENTER">
E-early M-midseason L-late</TD>
</TR>
</TABLE>
</xsl:template>
<xsl:template match="daylily">
<TR>
<TD><xsl:value-of select="award/year"/></TD>
<TD><xsl:value-of select="cultivar"/></TD>
< ! — Выбор bloom. — >
<TD ALIGN="CENTER"><xsl:value-of select="bloom/@code"/></TD>
<TD ALIGN="RIGHT"><xsl:value-of select="cost"/></TD>
</TR>
</xsl:template>
</xsl:stylesheet>

Листинг 23.18.Daylilies.j sp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<html>
<head>
<title>Daylilies</title>
</head>
<body>
<%@ taglib uri="cwp-tags/xsltransform.tld" prefix="cwp" %>

<H1 ALIGN="CENTER">Katie's Favorite Daylilies


<P>
<cwp:xsltransform xml='perennials.xml'
xslie='perennials-ie.xsl'
xslns='perennials-ns.xsl'
/>
</body>
</html>

Листинг 23.19.xsltransform.tld

<?xml version="1.0" encoding="ISO-8859-l" ?>


<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN'
"http://Java.sun.com/j2ee/dtds/web-jsptaglibrary_l_l.dtd">

<taglib>
1094 Глава 23. Обработка XML-доку ментов

<tlibversion>l.0</tlibversion>
<jspversion>l. К/jspversion>
<shortname>cwp</shortname>
<urn></urn>
<info>
A tag library from Core Web Programming,
http://www.corewebprogramming.com/.
</info>

<tag>
<name>xsltransform</name>
<tagclass>cwp.tags.XslTransformTag</tagclass>
<info>Applies xslt transform based on browser type.</info>
<attribute>
<name>xml</name>
<required>yes</required>
</attribute>
<attribute>
<name>xslie</name>
<required>false</required>
</attribute>
<attribute>
<name>xslns</name>
<required>true</required>
</attribute>
</tag>
</taglib>

Листинг 23.20.XslTransformTag.Java

package cwp.tags;

import java.io.*;
import javax.servlet.*;
import javax.servlet.jsp.*;
import javax.servlet.http.*;
import javax.servlet.j sp.tagext.*;
import javax.xml.transform.*;
import cwp.XslTransformer;

/** Дескриптор, предназначенный для преобразования


* XML-документа в HTML-формат, используя XSLT.
* В зависимости от типа броузера выбирается XSL-код,
* ориентированный либо на Internet Explorer, либо
* на Netscape (принимается по умолчанию).
V
public class XslTransformTag extends TagSupport {
private static final String FS =
System.getProperty("file.separator");
private static final int IE = 1;
private static final int NS = 2;
private String xml, xslie, xslns;
2 3 . 8 . Пример использования XSLT... 1095

public void setXml(String xml) {


this.xml = xml;
}
public String getXml() {
return(xml);
}
public void setXslie(String xslie) {
this.xslie = xslie;
}

public String getXslieO {


return(xslie);
}
public void setXslns(String xslns) {
this.xslns = xslns;
}

public String getXslnsO {


return(xslns);
}
public int doStartTagO throws JspException {
// Определение пути к исходным файлам XML и XSL.
/ / З а расположение исходных файлов принимается
// SERVLET_HOME/WEB-INF/classes/cwp/.
String FS = System.getProperty("file.separator");
ServletContext context = pageContext.getServletContext();
String path = context.getRealPath(FS) + "WEB-INF" + FS +
"classes" + FS + "cwp" + FS;

HttpServletRequest request =
(HttpServletRequest)pageContext.getRequest();

// В зависимости от типа броузера выбираются листы


// стилей для Internet Explorer либо для Netscape.
File xslFile = null;
if ((browserType (request) == IE) && (getXslieO ?= null)) {
xslFile = new File (path -»- getXslieO);
} else {
xslFile = new File (path + getXslnsO);
}
File xmlFile = new File (path + getXmlO);
try {
JspWriter out = pageContext.getOut0;
XslTransformer transformer = new XslTransformer();
transformer.process(xmlFile, xslFile, out);

catch(TransformerException tx) {
context.log("XslTransformTag: " + tx.getMessage());
}
return(SKIP_BODY);
}
1096 Глава 23. Обработка XML-документов

// Определение типа броузера по значению поля


// User-Agent, содержащегося в заголовке HTTP-запроса.
private int browserType(HttpServletRequest request) {
int type = NS;
String userAgent = request.getHeader("User-Agent");
if ((userAgent != null) &&
(userAgent.indexOf("IE") >=0)) {
type = IE;
}
return(type);

b-^^lS, '. ji

j 4-Bdck * «4 ^-^ S\ ^ ':0Search ^yFavorites ^^Htrtory i^-

^
Katie^s Favorite Daylilies
Stout Medal Award

jYear Cultivar Bloom Season Cost


|1965 Luxury Lace M 11.75
il976 Green Flutter M 7.50
11984 My Bene E 12.00
|1985 Stella DeOro E-L 5.00 Рис. 23.11. Преобразование
11989 Brocaded Gown E 14.50 документа p e r e n n i a l s . xml с
E-eariy M-midseason L-late применением пользовательского
JSP-дескриптора. Результаты
преобразования отображаются в окне
Internet Explorer 5.0, выполняющегося
IgjOone в системе Windows 2000

ш:\пЩ
Ш Ы )£ifw Шо Sioemxm^it НФ
а ^ т ^ ^ ^ Ш ' ^ rfa
Katie^s Favorite Daylilies
Stout Medal Award

lYear i Cultivar jBloom Season j Cost |


h 965 I Luxury Lace M 111.75;
11976 i Green Flutter M i 7.50 1
11984 iMy Belle E 112.00;
1985 SteOaDeOro | E-L \ 5.00;
Рис. 23.12. Преобразование документа
11989 i Brocaded Gown 1 E i 14.50 i
p e r e n n i a l s . xml С применением поль­
E-early M-midseason L-late зовательского JSP-дескриптора.
Результаты преобразования отобра­
жаются в окне Netscape 4.7, выполня­
f^^r- ,DoB«i4»itD»» 2iM^m ME^iM.^^^J л ющегося в системе Windows 98
23.8. Пример использования XSLT... 1097

23.9. Резюме
Данная глава завершает часть III, посвященную созданию программ, предназна­
ченных для выполнения на стороне сервера. Теперь вы знаете, как обрабатывать
XML-файлы с помощью DOM, SAX и XSLT. Вы также умеете создавать сервлеты и
JavaServer Pages, выполнять НТТР-туннелирование и организовывать взаимодействие
с базами данных посредством JDBC. Несомненно, что вы уже пробовали применять
полученные знания на практике и ваш начальник наверняка оценил результаты.
(А может быть, вы и сами уже стали начальником?)
В следующей части книги мы вернемся к рассмотрению программ, выполняющих­
ся на стороне клиента, и обсудим JavaScript — интерпретируемый язык, предназна­
ченный для написания сценариев, которые запускаются в среде броузера. Примене­
ние JavaScript-сценариев позволяет сделать Web-страницу более гибкой и динамич­
ной. В частности, JavaScript-сценарии очень часто применяются для проверки
данных, введенных посредством HTML-формы, перед передачей их на сервер.
-JzJS^-r±

JAVASCRIPT
В ЭТОЙ части...

Глава 24. JavaScript: динамически изменяющиеся Web-страницы


Глава 25. Краткое руководство по JavaScript
JAVASCRIPT:
ДИНАМИЧЕСКИ
ИЗМЕНЯЮЩИЕСЯ
WEB-СТРАНИЦЫ

В ЭТОЙ главе...

Генерация HTML-кода при загрузке Web-страницы.

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

Создание сценариев, не зависящих от платформы.

Синтаксис JavaScript.

Использование JavaScript для настройки Web-страниц.

Использование JavaScript для включения динамических


компонентов на Web-страницу.

Использование JavaScript для проверки CGI-форм.

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

Обращение к Java из JavaScript.

Обращение к JavaScript из Java.


J~y\3J^ZJ

Н есмотря на сходство имен, JavaScript существенно отличается от Java.


JavaScript -- это язык сценариев; фрагменты программ на JavaScript встраива­
ются на Web-страницы и выполняются при загрузке документов. Java— это
язык программирования общего назначения, который можно использовать для напи­
сания приложений, программ, выполняющихся на стороне сервера, и аплетов, рабо­
тающих в среде броузера. JavaScript-сценарию доступны практически любые сведения
об HTML-документе, он может выполнять различные действия с элементами, кото­
рые содержатся в этом документе. Аплет, включенный в документ, изолирован от ос­
тальных элементов Web-страницы. JavaScript-сценарий не может пользоваться графи­
ческой библиотекой, создавать запросы или поддерживать сетевые соединения. Про­
граммисту, пишущему Java-программы, доступны мощные графические средства
(AWT, Swing, Java 2D), классы для поддержки потоков и сетевого обмена, включая
гнезда, RMI и JDBC.
В настоящее время существуют шесть версий JavaScript (от 1.0 до 1.5) и седьмая вер­
сия JavaScript 2.0, предложенная Mozilla Organization ( h t t p : / / w w w . m o z i l l a . o r g / ) .
Версия JavaScript 1.0 была разработана компанией Netscape и реализована в составе
Netscape Navigator 2.0. После этого Netscape передала JavaScript ЕСМА (European Com­
puter Manufacturers Association) для составления стандартов. В июне 1997 г. ЕСМА соз­
дала стандарт ECMAScript, ЕСМА-262, который в полном объеме поддерживается
JavaScript 1.1, реализованным в составе Navigator З.Ох. В дальнейшем Netscape перестала
следовать стандарту ЕСМА и в Navigator 4.0-4.05 реализовала JavaScript 1.2. В августе
1998 г. ЕСМА выпустила вторую редакцию ЕСМА-262, которая соответствовала стандар­
ту ISO/IEC 16262. Вторая редакция стандарта ECMAScript была реализована в Navigator
4.06 как JavaScript 1.3. В этой версии были внесены изменения в некоторые объекты и
включены константы I n f i n i t y и NaN. В последующей версии JavaScript 1.4 полностью
поддерживалась обработка исключений. Обработка исключений и улучшенная под­
держка национальных кодировок были учтены в третьей редакции стандарта ЕСМА-262,
выпущенной в декабре 1999 г. Последняя версия броузера, Netscape Navigator 6, включа­
ет версию JavaScript 1.5, в полном объеме реализующую третью редакцию ECMAScript.
1102 Глава 24. JavaScript...

Текущее положение дел с JavaScript несколько осложняется тем, что Microsoft вы­
пустила собственную реализацию стандарта ECMAScript под названием JScript. Не­
смотря на то что Microsoft предоставляет различные платформы для выполнения
сценариев (Internet Information Server, Windows Scripting Host, Visual Studio), под­
держивающие разные версии JScript, мы вкратце рассмотрим лишь версии JScript,
поддерживаемые Internet Explorer. Средства JavaScript 1.1 в основном реализованы в
Internet Explorer З.Ох; полную поддержку JavaScript 1.2 и соответствие второй редак­
ции ЕСМА-262 обеспечивает Internet Explorer 4.Ox. Internet Explorer 5.0x поддержива­
ет JavaScript 1.3 и большинство средств JavaScript 1.4. В Internet Explorer реализованы
полная поддержка третьей редакции ЕСМА-262 и спецификации JavaScript 1.5.
Основные языковые средства JavaScript с момента создания версии 1.2 не претер­
пели существенных изменений и поддерживаются практически всеми популярными
броузерами (в частности, Netscape 4, Internet Explorer 4 и более поздними версиями).
Рассмотрению этих языковых средств посвящены эта и следующая главы. DOM
(Document Object Model — объектная модель документа) для реализации DHTML об­
суждаться не будет. Сведения о DOM и различных версиях языков сценариев вы най­
дете по следующим адресам.

С п е ц и ф и к а ц и я ECMAScript
h t t p : / / w w w . e c m a . c h / e c m a l / S T A N D / E C M A - 2 62.HTM

И н ф о р м а ц и я д л я р а з р а б о т ч и к о в , и с п о л ь з у ю щ и х JavaScript,
предоставляемая Netscape
http://developer.netscape.com/tech/javascript/

Microsoft JScript
http://msdn.microsoft.com/scripting/

И н ф о р м а ц и я , п р е д о с т а в л я е м а я Mozilla Organization
http://www.mozilla.org/js/

D o c u m e n t Object M o d e l
http://www.w3.org/DOM/

Дополнительную информацию о JavaScript вы можете найти в работе Р. Аллена


Вайка (R. Allen Wyke), Джейсона Д. Гильяма (Jason D. Gilliam) и Чарлтона Тинга
(Charlton Ting) Pure JavaScript (JavaScript 1.4), a также в работе Денни Гудмена (Danny
Goodman) Dynamic HTML, The Definitive Reference (jdiVdScvipi 1.2).
Использовать JavaScript на Web-страницах можно двумя способами. Во-первых, вы
можете динамически создать HTML-документ в процессе загрузки страницы. Во-
вторых, с помощью JavaScript вы можете отслеживать различные события и выпол­
нять необходимые действия. Эти подходы описаны в двух первых разделах данной
главы. Задачи, выполняемые JavaScript, условно разделяются на семь основных кате­
горий: изменение содержимого Web-страниц, обеспечение динамического поведения
2 4 . 1 . Д и н а м и ч е с к а я г е н е р а ц и я HTML-документов 1103

документов, проверка CGI-форм, обработка cookie, взаимодействие с фреймами, об­


ращение к Java из JavaScript и к JavaScript— из Java-программ. Каждая из этих катего­
рий задач будет обсуждаться в данной главе. Глава 25 посвящена детальному рассмот­
рению стандартных объектов, поддерживаемых JavaScript 1.2.

2 4 . 1 . Динамическая генерация
НТМ L-документов
Код JavaScript-сценария содержится в элементе SCRIPT и выполняется в процессе
загрузки Web-страницы. Выходные данные, генерируемые сценарием, включаются в
том месте документа, где находится элемент SCRIPT. Формат JavaScript-сценария
приведен в листинге 24.1. Если какие-то из особенностей синтаксиса JavaScript вам
непонятны, не стоит беспокоиться, мы рассмотрим их в конце данного раздела.

Листинг 2 4 . 1 . JavaSchpt-сцендрий в составе Web-страницы

<BODY>
HTML-текст

<SCRIPT T y P E = " t e x t / j a v a s c r i p f •>

Java Script-код, предназначенный для генерации HTML-содержимого


// —>
</SCRIPT>

HTML-текст
</BODY>

Вы также можете загружать удаленный JavaScript-код; для этого используется атри­


бут SRC дескриптора <SCRIPT>.
Самый простой способ динамического создания HTML-документа состоит в ис­
пользовании метода d o c u m e n t . w r i t e , который включает в текущий документ строку
символов. П р и м е р использования этого метода приведен в листинге 24.2, а результа­
ты показаны на рис. 24.1.

Листинг 2 4 . 2 . F i r s t S c r i p t . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 / / E N " >


<HTML>
<HEAD>
< T I T L E > F i r s t J a v a S c r i p t Page</TITLE>
</HEAD>
<BODY>
< H l > F i r s t J a v a S c r i p t Page</Hl>

<SCRIPT T Y P E = " t e x t / j a v a s c r i p t " >


1104 Глава 2 4 . JavaScript..

document. write ("<HR>") ;


dociment.write ("Hello World Wide Web") ;
document.write("<HR>");
// — >
</SCRIPT>

</BODY>
</HTML>

laSctipl Page • Netscape


gle £сй View fio Qonmrec^tor й ф

Ш
First JavaScript Page
HeUo World Wide Web
Рис. 24.1. Горизонтальные линии и текст
между ними сгенерированы JavaScript-
^^^^p„. ^b:^6>^iiD^";-l 'jy;;^]|ycgguvjsag;^,.. сценарием

В данном случае JavaScript-сценарий не делает ничего нового по сравнению с


обычным HTML-кодом. Гораздо чаще JavaScript-код используется для создания раз­
личных HTML-элементов в зависимости от конкретных обстоятельств. Пример тако­
го сценария приведен в листинге 24.3, а на рис. 24.2 и 24.3 показаны результаты его
выполнения соответственно в среде броузеров Netscape Navigator и Microsoft Internet
Explorer. Обратите внимание на вспомогательную функцию r e f e r r i n g Page и ис­
пользование знака "+" для конкатенации строк. Одна длинная строка, составленная с
помощью операций конкатенации, обычно проще для понимания, чем последова­
тельность вызовов document . w r i t e для каждой строки документа. Заметьте также,
что использование метода document . w r i t e l n вместо document . w r i t e позволяет
избежать включения символа "\п" в каждую строку текста. Символ перевода строки
никак не влияет на внешний вид Web-страницы, отображаемой броузером, однако
часто возникает необходимость просматривать исходный код HTML-документа в от­
дельном окне. Отображение исходного кода Web-страницы в отдельном окне очень
удобно при отладке. Вы можете копировать фрагменты кода в отдельный файл и про­
верять его корректность с помощью специальных инструментальных средств (см.
раздел L3).

М е т о д и к а профессионалов

Если ваш броузер позволяет отображать исходный текст HTML-до-


кумента, его можно использовать для проверки корректности сгене­
рированного HTML-кода.
2 4 . 1 . Динамическая генерация HTML-доку ментов 1105

Листинг 24.3. ShowInfo.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Extracting Document Info with JavaScript</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>Extracting Document Info with JavaScript</Hl>
<HR>

<SCRIPT TYPE="text/javascript">
<? —

function referringPage() {
if (docioment. ref errer. length = = 0 ) {
return("<I>none</I>");
} else {
return (dociiment. referrer) ;
}
}

document.writeln
("Document Info:\n" +
"<UL>\n" +
" <LIXB>URL: </B> " + document. location + "\n" +
" <LIXB>Modification Date:</B> " + "\n" +
document.lastModified + "\n" +
" <LIXB>Title:</B> " + docianent. title + "\n" +
" <LIXB>Ref erring page:</B> " + ref err ingPage () + "\n" +
"</UL>");
document.writeln
("Browser Info:" + "\n" +
"<UL>" + "\n" +
" <LIXB>Name:</B> " + n a v i g a t o r . appName + " \ n " +
" <LIx:B>Version:</B> " + navigator.appVersion + "\n" +
"</UL>");

// — >
</SCRIPT>

<HR>
</BODY>
</HTML>
1106 Глава 24. JavaScript...

• JDixi|
yniHi'iiiiiniiniJiHiiimiifffwiHi'nm

Extracting Document Info with JavaScript


Document Info:

• URL: http://www.corewebprogramming.coin/JavaScnpt/ShowInfo.html
• Modification Date: Wednesday, April 11, 2001 17:19:10
• Title: Extracting Document Info with JavaScript
• Referring page: none

Browser Info:

• Name: Netscape
• Version: 4.7 [en] (Win98.1)

iDocument: Oortt .•^...<^Ё....

Рис. 24.2. Результаты выполнения сценария showinfo, отображае­


мые в окне броузера Netscape 4.7, который выполняется в среде
Windows 98

шш^
"^
Extracting Document Info with JavaScript
Document Info:

• URL: http ://www. с orewebprogramming. с om/JavaS cript/Showinfo html


. Modification Date: 04/11/2001 17:19:10
• Title: Extracting Document Info with JavaScript
• Referring page: none

Browser Info:

• Name: Microsoft Internet Explorer


• Version: 4.0 (compatible; MSIE 5 5. Windows 98)

d
Щ2 0опе Д | My Computer

Рис. 24.3. Результаты выполнения сценария showinfo, отобража­


емые в окне броузера Internet Explorer 5.0, который выполняется в
среде Windows 98
2 4 . 1 . Динамическая генерация HTML-документов 1107

Обеспечение совместимости с различными


броузерами
Заметьте, что JavaScript-код оформляется как HTML-комментарии. Так поступать
не обязательно, но этот подход используется довольно часто. Дело в том, что старые
броузеры, не поддерживающие JavaScript, игнорируют дескрипторы <SCRIPT> и
</SCRIPT>, но отображают текст, содержащийся между ними. Представление кода
сценария как комментариев предотвращает его появление в окне. Заметьте, что
JavaScript интерпретирует последовательности " / / " и "<! - - " как начало комментари­
ев, состоящих из одной строки. Подобная стратегия не защищает от ошибок, по­
скольку некоторые последовательности символов в тексте сценария могут быть не­
правильно и н т е р п р е т и р о в а н ы старыми броузерами. Например, согласно специфика­
ции HTML 2.0, комментарии должны находиться между парами символов " - - " ,
которые, в свою очередь, помещаются между "< !" и ">". Так, например, комментарии
< ! — Foo — — Bar —>
составлены корректно, а
< ! — Foo — Bar —>
недопустимы. Соответственно следующие фрагменты кода, согласно HTML 2.0, пред­
ставляют собой некорректно созданные комментарии:
<! —
var X = 3;
if (х—>2) // Некорректно
doOneThing();
else
doAnotherThing();
// — >
<! —
var X = 3;
var у = х - - ; // Некорректно
/ / —>
Может показаться странным, но JavaScript не сообщает текущую версию языка, по­
этому часто приходится вручную указывать версию, как это показано в следующих
примерах:
<SCRIPT LANGUAGE="JavaScript">
<! —
languageVersion = "1.0";
// — >
</SCRIPT>

<SCRIPT LANGUAGE="JavaScriptl.l">
<! —
languageVersion = "1.1";
// — >
</SCRIPT>

<SCRIPT LANGUAGE="JavaScriptl.4">
<! —
1108 Глава 24. JavaScript.

languageVersion = "1.4";
// — >
</SCRIPT>

<SCRIPT LANGUAGE="JavaScriptl.5">
<! —
languageVersion = "1.5";
// — >
</SCRIPT>
Выполняя этот пример, не используйте атрибут TYPE=" t e x t / j a v a s c r i p t " .
В большинстве броузеров наличие атрибута TYPE приводит к выполнению сценария
независимо от версии JavaScript. В Netscape 6 эта проблема не возникает.

24.2. Обработка событий


JavaScript не только дает возможность создавать HTML-код при загрузке Web-
страницы, но также позволяет связывать с HTML-элементами фрагменты кода. Эти
фрагменты получают управление при выполнении пользователями некоторых дейст­
вий. Таким образом, вы можете отслеживать различные события: щелчок мышью на
кнопке или на гипертекстовой ссылке, загрузку документа или прекращение работы с
ним, указание курсором мыши на ссылку и перемещение курсора за ее пределы, полу­
чение или потерю элементом фокуса ввода, передачу данных программе на стороне
сервера и возникновение ошибки при загрузке изображения. В листинге 24.4 показан
документ, в котором функция d o n t C l i c k связывается с кнопкой посредством атрибу­
та o n C l i c k . Результаты выполнения сценария показаны на рис. 24.4 и 24.5. Кнопка
(элемент BUTTON) включена в состав документа лишь для демонстрации возможно­
стей JavaScript; подобный обработчик может быть связан с любым стандартным
HTML-элементом. Заметьте, что код функции d o n t C l i c k содержится не в теле доку­
мента (разделе BODY), а в разделе HEAD. Данный подход широко применяется при оп­
ределении функций, которые непосредственно не генерируют HTML-код.

Листинг 2 4 . 4 . D o n t C l i c k . html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Simple J a v a S c r i p t B u t t o n < / T I T L E >
<SCRIPT T Y P E = " t e x t / j a v a s c r i p t " >

function dontClick0 {
a l e r t ( " I t o l d you n o t t o c l i c k ! " ) ;
}
/ / —>
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>Simple JavaScript Button</Hl>
<FORM>
24.3. Синтаксис JavaScript 1109

<INPUT TYPE="BUTTON"
VALUE="Don't Click Me"
onClick="dontClick() ">
</FORM>
</BODY>
</HTML>
^»jjijU;p|MI|^juUt
t^nfflffimfmrfflf шшт
"D
Simple JavaScript Button
~ сьлаа^ш.
d
.C]l>one ;^Ы;>Ос*ч»««

Рис. 24.4. Внешний вид Web-страницы Рис. 24.5. Сообщение, отображаю­


перед щелчком на кнопке щееся после щелчка мышью на кнопке

24.3. Синтаксис JavaScript


Синтаксис JavaScript напоминает синтаксис языков Java и С. Многие языковые
конструкции уже знакомы вам, например, операторы i f , "? :", w h i l e , f o r , b r e a k и
c o n t i n u e используются так же, как и в языке Java. В JavaScript 1.2 был добавлен опе­
ратор s w i t c h , который напоминает одноименный оператор Java, за исключением то­
го, что значения, указываемые после c a s e , не обязательно должны быть целыми.
Операторы + (сложение чисел и конкатенация строк), - , *, / , ++, - - , &&, | | и другие
используются практически так же, как и соответствующие операторы в языке Java.
О п е р а т о р ы не обязательно должны заканчиваться точкой с запятой, однако, в при­
мерах мы используем данный символ для того, чтобы код программы лучше воспри­
нимался. Здесь мы рассмотрим некоторые общие характеристики языка; детальное
описание будет приведено в следующей главе.
Заметьте также, что Netscape предоставляет удобное окно для ввода и вычисления
JavaScript-выражений (рис. 24.6). Чтобы открыть его, надо ввести URL " j a v a s c r i p t : " .
Кроме того, выражения можно вычислять, непосредственно задавая их в строке Loca­
tion (например, " j a v a s c r i p t .-Math, c o s (Math. P I / 4 ) *2"); результаты отображаются в
окне броузера. Такой подход применим как для Netscape, так и для Internet Explorer.
|^"(afx^|
p-ffwfffUHim^
1.414213S6237309S1

javascnpt typein

JKath. cos (Math. PI/4) *2|


Clear Console I Close I

Рис. 24.6. Броузер Netscape предоставляет интерактивное окно для


ввода и вычисления JavaScript-выражений
1110 Глава 24. JavaScript...

Динамическая поддержка типов


Одно из основных отличий между Java и JavaScript заключается в том, что в
JavaScript не объявляются типы переменных. Это относится к локальным перемен­
ным, переменным экземпляра (которые согласно терминологии JavaScript называют­
ся свойствами) и даже к значениям, возвращаемым функциями. Более того, в процес­
се работы сценария тип переменной может изменяться. Например, следующий фраг­
мент кода создан правильно:
v a r X = 5 ; / / Целое число
X = 5 . 5 ; / / Число с плавающей точкой
X = " f i v e p o i n t f i v e " ; / / Строка

Объявление функций
Для объявления функции используется ключевое слово f u n c t i o n . П р и этом не
указываются ни тип возвращаемого значения, ни типы параметров. П р и м е р ы функ­
ций приведены ниже.
function square(х) {
return(х * х ) ;
}
function factorial(n) {
if (n <= 0) {
return(1);
} else {
return(n * factorial(n - 1));
}
}

function printHeading(message) {
document.writeln("<Hl>" + message + "</Hl>");
}
Функции могут присваиваться переменным, например:
: fun = Math.sin;
^rt("sin(pi/2)=" + fun(Math.PI/2));
Результаты выполнения данного фрагмента показаны на рис. 24.7.

Л JavaiSaipt Alert
stn(pi/2)-1

Рис. 24.7. JavaScript позволяет присваивать функцию переменной,


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

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


это показано в следующем примере. Однако при написании реальных приложений
использовать такой подход не следует.
24.3. Синтаксис JavaScript 1111

Math.sin = Math.cos; / / He делайте этого в реальных программах


alert("Yikes! sin(pi/2)=" + Math.sin(Math.PI/2));
Результаты приведены на рис. 24.8.

|ГГ7ЖГ111 1

Рис. 24.8. JavaScript позволяет переопре­


делять стандартные функции

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

JavaScript позволяет добавлять свойства в процессе


выполнения программы
Для того чтобы добавить свойство (поле), достаточно присвоить ему значение.
Если свойство не существует, оно будет создано. Например:
var t e s t = new O b j e c t O ;
t e s t . f i e l d l = "Value 1"; / / Создание свойства f i e l d l
test.field2 = 7 ; / / Создание свойства f i e l d 2
Несмотря на то что такой подход упрощает создание новых свойств, он в то же
время "маскирует" ошибки. Неверный символ в имени приведет к тому, что будет соз­
дано новое поле. Если вы попытаетесь обратиться к полю, которое не определено, вы
получите значение undefined. При сравнении (оператор ==) со значением n u l l
возвращается t r u e .

JavaScript позволяет задавать поля в литеральном виде


Поля можно задавать в "литеральном" виде, используя выражение
{ поле!:значение!, поле2:значение2, ... полеЫ:значением }
В приведенном ниже примере оЬ j ect 1 и ob j ect2 получают одинаковые значения.
var objectl = new ObjectO;
object1.x = 3;
objectl.у = 4;
objectl.z = 5;
var object2 = { x:3, y:4, z:5 };
1112 Глава 24. JavaScript...

Для перебора значений может использоваться


конструкция for/in
в отличие от Java и C++, в JavaScript существует языковая конструкция, позволяю­
щая перебирать все поля объекта. Формат выражения f o r / i n показан ниже.
for(fieldName in object) {
doSomethingWith(fieldName);
}

Зная имя поля (например, f i e l d ) , вы можете обратиться к нему не только с помо­


щью выражения o b j e c t . f i e l d , но и посредством o b j e c t [ " f i e l d " ] . Такая возмож­
ность позволяет перебирать поля объекта в цикле. Пример перебора полей показан в
листинге 24.5. В сценарии определена функция общего назначения, позволяющая соз­
дать HTML-таблицу, в которой отображаются поля заданного объекта. Результаты вы­
полнения сценария в среде броузера Internet Explorer 5.0 показаны на рис. 24.9.

Листинг 2 4 . 5 . F o r i n . html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>For/In Loops</TITLE>
<SCRIPT TYPE="text/javascript">
<! —
f u n c t i o n makeObjectTable(name, o b j e c t ) {
d o c \ a a e n t . w r i t e l n ( " < H 2 > " + name + "</H2>");
doc\iment.writeln("<TABLE BORDER=l>\n" +
" <TRXTH>Field<TH>Value") ;
f o r ( f i e l d in object) {
document, w r i t e l n (" <TRXTD>" + f i e l d +
"<TD>" + o b j e c t [ f i e l d ] ) ;
}
dociament.writeln("</TABLE>") ;
}
// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>For/In Loops</Hl>
<SCRIPT TYPE="text/javascript">
<! —
var test = new Object();
test.fieldl = "Field One";
test.field2 = "Field Two";
test.fields = "Field Three";
makeObjectTable("test", test);
// — >
24.3. Синтаксис JavaScript 1113

</SCRIPT>

</BODY>
</HTML>

Конструктор — это лишь функция, которая присваивает


значения переменой this
в JavaScript отсутствует полный аналог классов Java. Максимум, что вы можете сде­
лать, — это определить функцию, которая присваивала бы значения свойствам, дос­
тупным посредством переменной t h i s . При использовании этой функции в операто­
ре new переменная t h i s связывается с новым объектом Object. Ниже приведен
пример простого конструктора класса Ship.
function Ship(x, у, speed, d i r e c t i o n ) {
t h i s . x = x;
t h i s . у = у;
t h i s . s p e e d = speed;
this.direction = direction;
}

Пользуясь ранее определенной функцией makeObjectTable и включив в раздел


BODY документа приведенные ниже строки кода, мы получим результат, показанный
на рис. 24.10.
var shipl = new Ship(0, О, 1, 90);
makeObjectTable("shipl", shipl);

:»'111Н11Н11М|||НМ]|'11^!1ЯШ1^1м14

1For/In Loops -^ i Ц
Ships
1 test shipl
1 1 Reld Value
Held Value
field 1 Reld One 1 |x |0
field2PieldTwo ;y 0
fields Reld ТЪгее 1 ;speed '1 1
1 jdirecbon '90 1
1^ d [ ^_^ d
Рис. 24.9. Выражение for/in позволяет Рис. 24.10. Использование конструк­
перебирать свойства объекта в цикле тора— это лишь упрощенный способ
создания объекта и присвоения значе­
ний свойств
1114 Глава 2 4 . JavaScript...

Метод — это свойство, значением которого является


функция
в JavaScript не предусмотрены специальные синтаксические правила для опреде­
ления методов. Вместо этого можно присвоить свойству функцию. Например, вари­
ант класса S h i p , содержащий метод move, выглядит следующим образом:

function degreesToRadians(degrees) {
return(degrees * Math.PI / 180.0);
}

function move() {
var angle = degreesToRadians(this.direction);
this.x = this.x + this.speed * Math.cos(angle);
this.у = this.у + this.speed * Math.sin(angle);
}
function Ship(x, y, speed, direction) {
this.x = x;
this.у = у;
this.speed = speed;
this.direction = direction;
this.move = move;
}

Обратите внимание на ключевое слово v a r перед переменной a n g l e — так в


JavaScript определяются локальные переменные. Если вы забудете указать данное
ключевое слово, сообщение об ошибке не появится, но интерпретатор JavaScript бу­
дет считать, что a n g l e — это свойство текущего окна. В этом случае возникнет кон­
фликт между переменными, определенными в разных функциях и имеющих одинако­
вые имена. Подобные проблемы трудно распознавать и устранять, поэтому необхо­
димо внимательно следить за использованием ключевого слова v a r .

М е т о д и к а профессионалов

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


ключевое слово var.

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


выполнения показаны на рис. 24.11.
v a r s h i p l = new S h i p ( 0 , О, 1, 9 0 ) ;
makeObjectTable("shipl ( o r i g i n a l l y ) " , shipl);
shipl.move();
makeObjectTable("shipl ( a f t e r move)", shipl);
24.3. Синтаксис JavaScript 1115

• ^

Ships
shipl (originally)

Field Value
X 0
У _ 0 _
speed 1
direcbon 90
fiinchon moveO { var angle = degreesToRadians
(tbs direction); this.x = this.x +this, speed * Math.cos
(angle); this у = this у + this.speed * Math. sin(angle);)

shipl (after move)


Field Value
X 6 123031769111886e-17
У ' ' 'i
speed 1
direction 90
function moveO { var angle = degreesToRadians
move (this direction), this.x = this.x + this.speed * Math.cos
(angle), this.y = this.у + this.speed * Math sin(angle),}

_J
^ Done '^ЩОщлЛт

Рис. 24.11. Методы представляют собой специ­


альный тип свойств

Для создания методов и свойств можно использовать


свойство prototype
Свойство p r o t o t y p e упрощает создание методов и свойств. Если у вас есть хотя
бы один объект определенного класса, то присвоение значений полям посредством
свойства p r o t o t y p e приведет к тому, что эти значения будут заданы для соответст­
вующих свойств всех объектов. Если свойство определено явно, значение не при­
сваивается. В следующем примере определен объект S h i p . Посредством свойства
p r o t o t y p e задаются значения полей move и m a x S p e e d объектов данного класса.
f u n c t i o n S h i p ( x , у, s p e e d , d i r e c t i o n ) {
t h i s . x = x;
t h i s . y = y;
this.speed = speed;
this.direction = direction;
}
new Ship(0, 0, 0, 0) ;
Ship.prototype.move = move;
Ship.prototype.maxSpeed = 50;
1116 Глава 24. JavaScript...

Массивы
в JavaScript массивы используются почти так же, как и в языке Java. Ниже приведены
примеры применения конструктора A r r a y , который упрощает создание массивов.
v a r s q u a r e s = new A r r a y ( 5 ) ;
for(var i=0; i<squares.length; i++) {
vals[i] = i * i;
}
/ / Создание м а с с и в а з а один э т а п ,
v a r s q u a r e s = new A r r a y ( 0 , 1, 4, 9, 1 6 ) ;
v a r a r r a y l = new A r r a y C ' f e e " , " f i e " , " f o " , "fum") ;
/ / Создание м а с с и в а в " л и т е р а л ь н о м " в и д е ,
v a r а г г а у 2 = [ " f e e " , " f i e " , " f o " , "fum" ] ;
Следуя подобному подходу, вы не испытаете затруднений при работе с массивами.
На самом деле в JavaScript массивы представляются в виде объекта с нумерованными по­
лями. Кроме того, вы можете пользоваться именованными полями, используя выраже­
ния в формате объект, поле либо объект [''поле"]. Для доступа к нумерованным полям мо­
жет применяться только выражение объект [ номер_поля]. Ниже приведен пример рабо­
ты с массивом. Результаты выполнения этого примера показаны на рис. 24.12.
v a r a r r a y O b j = new O b j e c t O ;
arrayObj[0] = "Index zero";
arrayObj[10] = "Index t e n " ;
a r r a y O b j . f i e l d l = " F i e l d One";
a r r a y O b j [ " f i e l d 2 " ] = " F i e l d Two";

makeObj e c t T a b l e ( " a r r a y O b j " , arrayObj);

щшшшЕватт
ВЧ £ * li9» ^^^тянжШиси Н Ф

i ^ ^ 3 й 4^ A ^ rf a Ш ffl
Array/Object Duality
arrayObj

Field Value
jo b d e x zero
Index ten
.field 1 Field One
|field2 Field Two

"lOw .E>J^-..~.v.;^-. A'~ -^.J.

Рис. 24.12. Массив представляет собой


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

В процессе работы желательно отказаться от обращения к массивам как к объектам;


значительно удобнее рассматривать массивы и объекты как различные языковые кон­
струкции. Однако в некоторых случаях (например, если строка символов используется в
качестве имени свойства) целесообразно вспомнить о том, что массив представляется в
виде объекта, и использовать при обращении к нему соответствующий формат. Пример
такого подхода приведен в функции makeOb j e c t T a b l e (листинг 24.5).
2 4 . 4 . Использование JavaScript при создании Web-страниц 1117

24.4. Использование JavaScript при создании


Web-страниц
Поскольку JavaScript позволяет определять параметры текущего броузера,
JavaScript-сценарии целесообразно использовать при создании Web-страниц, харак­
теристики которых должны зависеть от применяемой клиент-программы. Так, на­
пример, дескрипторы, включаемые в HTML-документы, могут отличаться в зависимо­
сти от того, ориентирован ли документ на Internet Explorer или на Netscape Navigator.
В других случаях информация, отображаемая на экране броузера, зависит от того, на
какой Web-странице была расположена ссылка, по которой пользователь вызвал те­
кущий документ. Если размеры окна броузера невелики, надо использовать изобра­
жения небольшого размера. Объекты следует включать в состав Web-страницы только
при наличии дополнительных модулей, предназначенных для их обработки. Если
клиент-программа, обращающаяся к Web-странице, находится в домене, принадле­
жащем вашей компании, желательно не отображать на странице сообщение о том,
что вы готовы рассмотреть предложения работодателей. Н и ж е рассмотрены средст­
ва, позволяющие определять размеры окна броузера, а также выявлять дополнитель­
ные модули в составе клиент-программы.

Определение размеров окна броузера


Netscape 4.0 предоставляет свойства w i n d o w . i n n e r W i d t h и w i n d o w . i n n e r -
H e i g h t , позволяющие определять текущие размеры окна броузера. В листинге 24.6
показан фрагмент JavaScript-кода, который, в зависимости от размеров окна клиент-
программы, устанавливает размеры изображения и выбирает шрифт. На рис. 24.13 и
24.14 показаны результаты выполнения сценария в различных окнах броузера.
Рассматривая данный пример, обратите внимание на то, что код функции поме­
щен в раздел HEAD, несмотря на то, что функция вызывается в разделе BODY. Подоб­
ный подход имеет ряд преимуществ. Во-первых, исходный текст сценария становится
более удобочитаемым. Во-вторых, в этом случае функция может многократно вызы­
ваться в различных местах Web-страницы. В-третьих, поскольку раздел HEAD распо­
ложен перед разделом BODY, функции, определенные в составе HEAD, остаются дос­
тупными, даже если пользователь прерывает процесс загрузки страницы или, не до­
жидаясь окончания загрузки, активизирует гипертекстовую ссылку. Это особенно
важно, если функция используется в качестве обработчика события.

Методика профессионалов ^^^'ШШк.


Определяйте JavaScnpt'0yHKi4HH в разделе HEAD, особенно если эти ш И И н Ь
функции используются для обработки событий. ^ЗШи^

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

На з а м е т к у

В JavaScript строка текста может помещаться как в одинарные, так и


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

Листинг 2 4 . 6 . s t r a w b e r r i e s , h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Strawberries</TITLE>

<SCRIPT TYPE="text/javascript">
<! —
function image(url, width, height) {
r e t u r n ( ' < I M G SRC="' + u r l + " " +
' WIDTH=' + w i d t h +
' HEIGHT=' + h e i g h t + ' > ' ) ;
}

function strawberryl(width) {
return(image("Strawberryl.gif", width, Math.round(width*l.323))
}

function strawberry2(width) {
return(image("Strawberry2.gif", width, Math.round(width*!.155))'
}
/ / —>
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE">

<HR>
<SCRIPT TYPE="text/javascript">
<! —
var imageWidth = window.innerWidth/4;
var fontSize = Math.min(7, Math.round(window.innerWidth/100));
}
document.writeln
('<TABLE>\n' +
<TR><TD>' + strawberryl(imageWidth) + '\n' +
<TH><FONT SIZE=' + fontSize + '>\n' +
"Doubtless God <I>could</I> have made\n' +
a better berry, but doubtless He\n' +
never did."</FONT>\n' +
<TD>' + strawberry2(imageWidth) + '\n' +
'</TABLE>');
// — >
</SCRIPT>
<HR>
2 4 . 4 . Использование JavaScript при создании Web-страниц 1119

strawberries are my favorite garden crop; a fresh


strawberry picked five minutes ago makes the dry and
woody grocery store variety seem like a <B>totally</B>
different fruit. My favorite varieties are Surecrop
and Cardinal.

</BODY>
</HTML>

>41тя?дп?г дэд
Ы
"Doubtless God
could hsi\e made
a better berry,
but doubtless He
never did."
Strawberries are my favonte garden crop; a fresh strawberry picked five minutes ago makes the dry and woody grocery
store variety seem like a totally different fruit My favonte vanebes are Surecrop and Cardinal

S ач4>я=
,A Ш ..?i(^ .Лк^.. Ш . л ^ , , .

Рис. 24.13. В большом окне отображаются изображения и шрифт большого размера

Я'ТШдЦПР???

^^ ^ -^ 9 А ^ «а Jfll
"Doubtless
. ^ Goi could
'U^i> kave made

Strawbemes are my favonte


garden crop, a fresh strawberry
picked five minutes ago makes the ,^| Рис. 24.14. Заголовок корректно отображается
даже в окне малого размера
1120 Глава 24. JavaScript...

Определение наличия дополнительных модулей


в Netscape (но не в Internet Explorer) JavaScript-сценарий может обращаться к масси­
ву n a v i g a t o r . p l u g i n s , который содержит информацию о дополнительных модулях,
включенных в броузер. Каждый элемент в этом массиве представляет собой объект
P l u g i n , который содержит свойства name, d e s c r i p t i o n , f i l e n a m e и l e n g t h и в со­
став которого входит массив объектов MimeType. В указанных свойствах содержатся
имя дополнительного модуля, его описание, имя файла, в котором находится модуль, и
число поддерживаемых MIME-типов. Каждый объект MimeType содержит свойства
t y p e (строка, определяющая МШЕ-тип, например " t e x t / h t m l " ) , d e s c r i p t i o n
(описание), e n a b l e d P l u g i n (объект P l u g i n , поддерживающий данный тип) и
s u f f i x e s (список расширений файлов, связанных с данным MIME-типом; расширения
в списке разделяются запятыми). Как было сказано в разделе 24.3, в качестве "индекса"
при обращении к массиву можно указывать текстовую строку. Пример кода, опреде­
ляющего, инсталлирован ли определенный дополнительный модуль, приведен ниже.
if (navigator.plugins["Cosmo Player 1.0"]) {
document.write('<EMBED SRC="coolWorld.vrml" . . . > " ' ) ;
} else {
document.write('This example requires VRML.');
}
Заметьте, что данный код позволяет получить сведения о конкретном модуле. Ес­
ли же вас интересует, находится ли в составе броузера модуль, поддерживающий кон­
кретный МШЕ-тип, вам надо использовать свойство n a v i g a t o r . m i m e T y p e s .
if (navigator.mimeTypes["application/postscript"]) {
addPostScriptLink();
}

Дополнительную и н ф о р м а ц и ю по этому вопросу вы найдете в разделе 25.19.


В листинге 24.7 описанный подход используется для построения таблицы, содер­
жащей сведения об имеющихся дополнительных модулях и MIME-типах, поддержи­
ваемых ими. Результаты выполнения данного примера, отображаемые в окне броузе­
ра Netscape 4.7, показаны на рис. 24.15.

ГЛистинг 14.7. Plugins.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Plug-ins Supported</TITLE>

<SCRIPT TYPE="text/javascript">
<! —

function printRow(plugin) {
document.write
(" <TR><TD>" + plugin.name + "\n" +
" <TD>" + plugin.description + "\n" +
<TD>");
document.write(plugin[0].type);
24.4. Использование JavaScript при создании Web-страниц 1121

for(var i=l; i<plugin.length; i++)


document.writeln("<BR>" + plugin[i].type);

// ~->
</SCRIPT>
</HEAD>
<BODY>
<Hl>Plug-ins Supported</Hl>

<SCRIPT TYPE="text/javascript">
<! —
if (navigator.appName == "Netscape") {
document.writeln
("<TABLE BORDER=l>\n" +
" <TR><TH>Plug-in\n" +
" <TH>Description\n" +
<TH>MIME Types Supported");
for(var i=0; Knavigator .plugins . length; i-H-b)
printRow(navigator.plugins[i]);
document.writeln
("\n</TABLE>");
}
// — >
</SCRIPT>

</BODY>
</HTML>

|[gJD|)CJ|

Я
Plugins Supported
Plugin Description MIME Types Supported
'Headspace Beatnik Headspace Player Stub for audio/x-rmf
Player Stub VI.0.0 1 Netsc^e Conimunicator laudio/rmf
|арр11са110пУх-1ф
iSmartDownload Plugin
:appEcation/idp
iappEcation/x-java-bean;version=l. 1.2
i^plicationyx-java-applet;version=l.l 2
Java Plug-in 1.3 for Netscape
iJava Plug-in 1.3 for ^application/x-java-bean;version=l 1 3
Navigator with JDK/JRE 1.3
iNetscape Navigator appIication/x-java-applet,version=l. 1 3
(DLL Helper)
apphcation/x-java-bean;version=l. 2
application/x-java-applet,version=l 2
iQiacken 99 for Windows IPA Plugin stub application/x-ipa-plugin
d
^Оосяяа^йЛ: Done

Рис. 24.15. Информация о дополнительных модулях, установлен­


ных в броузере Netscape 4.7
1122 Глава 24. JavaScript.

24.5. Использование JavaScript для


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

Динамическое изменение изображений


Свойство d o c u m e n t . i m a g e s содержит массив объектов Image; каждый элемент
этого массива соответствует элементу IMG текущего документа. Ч т о б ы отобразить но­
вое изображение, надо изменить свойство SRC текущего изображения, установив зна­
чение этого свойства равным требуемому URL. Например, приведенная ниже функ­
ция изменяет первое изображение в составе документа.

function changelmage() {
document.images[0].src = "images/new-image.gif";
.}

Эта функция может быть вызвана из обработчика события (например, обработчи­


ка, который вызывается после щелчка на кнопке) или автоматически получать управ­
ление по истечении определенного интервала времени. Обращаться к изображению
по номеру не очень удобно, поскольку при включении в документ нового изображе­
ния приходится переписывать текст сценария. К счастью, JavaScript поддерживает
атрибут NAME элемента IMG. Например, если вы зададите имя изображения

<IMG SRC="cool-image.jpg" NAME="cool"


WIDTH=75 HEIGHT=25>
TO сможете обращаться к соответствующему элементу массива с помощью этого имени:
f u n c t i o n improvelmage() {
document.images["cool"].src = "way-cool.jpg";
}

Кнопка с изображением
Возможность динамически изменять изображение на Web-странице применяется
при создании кнопок. Функция c l i c k B u t t o n , код которой приведен ниже, временно
отображает новое изображение, а затем, по прошествии 1/10 секунды, снова восста­
навливает старую картинку. Это осуществляется с помощью функций s e t Image и
s e t T i m e o u t . Код функции s e t l m a g e приведен перед кодом c l i c k B u t t o n , а
24.5. Использование JavaScript для изменения... 1123

setTimeout представляет собой встроенную функцию JavaScript. При вызове


s e t Timeout ей передаются строка, содержащая JavaScript-выражение, и время в милли­
секундах. Функция setTimeout немедленно возвращает управление, но перед этим за­
пускает фоновый процесс, который ожидает окончания заданного интервала времени,
а затем выполняет выражение, содержащееся в строке.
function setlmage(name, image) {
document.images[name].src = image;
}

function clickButton(name, graylmage) {


var origlmage = document.images[name].src;
setImage(name, graylmage);
var resetString =
"setlmageC" + name + " \ '" + origlmage + " ' ) " ;
setTimeout(resetString, 100);
}
Для того чтобы с помощью приведенной выше функции создать кнопку с изобра­
жением, надо, во-первых, связать функцию с кнопкой, а во-вторых, убедиться, что
требуемые изображения находятся в кэше броузера. Для связывания изображения с
кнопкой используется атрибут onCliclc дескриптора <А>.
<А HREF="locationl.html"
onClick="clickButton('Buttonl*,
'images/Buttonl-Down.gif')">
<IMG SRC="images/Buttonl-Up.gif" NAME="Buttonl"
WIDTH=150 HEIGHT=25></A>

<A HREF="location2.html"
onClick="clickButton('Button2',
* images/Button2-Down.gif')">
<IMG SRC="images/Button2-Up.gif" NAME="Button2"
WIDTH=150 HEIGHT=25></A>

Чтобы после щелчка на кнопке новое изображение отображалось без задержки,


оно должно быть загружено и храниться в кэше. Для того чтобы обеспечить загрузку
изображения, надо создать объект Image (см. раздел 25.12) и установить свойство SRC
этого объекта. Как ни странно, в дальнейшем этот объект Image не будет использо­
ваться; он нужен только для того, чтобы броузер загрузил изображение и сохранил
его в кэше.
imageFiles = new Array("images/Buttonl~Up.gif",
"images/Buttonl-Down.gif",
"images/Button2-Up.gif",
"images/Button2-Down.gif");
imageObjects = new Array(imageFiles.length);

for(var i=0; i<imageFiles.length; i++) {


imageObjects[i] = new Image(150, 25);
imageObjects[i].src = imageFiles[i];
}
1124 Глава 24. JavaScript...

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

Листинг 24.8. ImageButton.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>JavaScript Image Buttons</TITLE>
<SCRIPT TYPE="text/javascript">

imageFiles = new Array("images/Buttonl-Up.gif",


"images/Buttonl-Down.gif",
"images/Button2-Up.gif",
"images/Button2-Down.gif");
imageObjects = new Array(imageFiles.length);
for(var i=0; i<imageFiles.length; i++) {
imageObjects[i] = new Image(150, 25);
imageObjects[i].src = imageFiles[i];
}
function setImage(name, image) {
document.images[name].src = image;
}
function clickButton(name, graylmage) {
var origlmage = document.images[name].src;
setlmage(name, graylmage);
var resetString =
"setlmageC" + name + "', '" + origlmage + " ' ) " ;
setTimeout(resetString, 100);
}
// — >
</SCRIPT>
</HEAD>
<BODY>
<Hl>JavaScript Image Buttons</Hl>
<A HREF="locationl.html"
onClic>:="clickButton('Buttonl', 'images/Buttonl-Down.gif')">
<IMG SRC="images/Buttonl-Up.gif" NAME="Buttonl"
WIDTH=150 HEIGHT=25></A>
<A HREF="location2.html"
onClic}<:= "clickButton ( 'Button2 ', ' images/Button2-Down. gif') ">
<IMG SRC="images/Button2-Up.gif" NAME="Button2"
WIDTH=150 HEIGHT=25></A>
</BODY>
</HTML>
24.5. Использование JavaScript для изменения... 1125

^^Подсветка" изображений при наведении указателя мыши


Часто средства модификации изображений применяются при создании картинок,
которые "подсвечиваются" при наведении на них указателя мыши и возвращаются к
исходному состоянию, когда указатель покидает пределы изображения. Применение
таких изображений улучшает внешний вид панелей инструментов. Однако, если та­
ких изображений много, явное указание каждого изображения в сценарии требует
дополнительных усилий. В листинге 24.9 показан пример, в котором работа упроща­
ется за счет соглашения об именовании. Имена файлов, содержащих обычное и
"подсвеченное" изображения, создаются на базе атрибута NAME элемента IMG (см.
функции r e g u l a r l m a g e F i l e и n e g a t i v e l m a g e F i l e ) . При этом исключается необ­
ходимость в создании массива имен для предварительной загрузки изображений и
передачи обработчику onMouseOver. Панели инструментов данного типа чаще всего
используются с фреймами. Структуры фреймов показаны в листингах 24.10 и 24.11.
Результаты выполнения сценария приведены на рис. 24.16. Если вы не помните
принципов работы с фреймами, прочитайте главу 4; это желательно сделать, по­
скольку фреймы очень часто используются с JavaScript-сценариями.

Листинг 2 4 . 9 . HxghPeaksNavBar.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD>
<TITLE>High P e a k s l ^ a v i g a t i o n Bar</TITLE>

<SCRIPT TYPE="text/javascript">
<l —

/ / Получая " F o o " , в о з в р а щ а е т "images/Foo.gif".

function regularlmageFile(imageName) {
r e t u r n ( " i m a g e s / " + imageName + " . g i f " ) ;
}

/ / Получая "Bar", возвращает "images/Bar-Negative.gif".

f u n c t i o n negativelmageFile(imageName) {
r e t u r n ( " i m a g e s / " + imageName + " - N e g a t i v e . g i f " ) ;
}

/ / Сохраняет в кэше изображение с заданным индексом.


/ / Например, по индексу О, обратившись к imageNames[0],
/ / получаем "Ноте". Затем загружаем изображения
/ / images/Ноте.gif и images/Home-Negative.gif.

f u n c t i o n cachelmages(index) {
r e g u l a r l m a g e O b j e c t s [ i n d e x ] = new Image(150, 2 5 ) ;
regularlmageObjects[index].src =
regularlmageFile(imageNames[index]);
n e g a t i v e l m a g e O b j e c t s [ i n d e x ] = new Image(150, 2 5 ) ;
negativelmageObjects[index].src =
negativelmageFile(imageNames[index]);
}
1126 Глава 24. JavaScript...

imageNames = new Array("Home", "Tibet", "Nepal",


"Austria", "Switzerland");
regularlmageObjects = new Array(imageNames.length);
negativelmageObjects = new Array(imageNames.length);

// Поместить изображения в кэш.


for(var i=0; i<imageNames.length; i++) {
cachelmages(i) ;
}

// Данная функция связывается с атрибутом onMouseOver;


// курсор представляется как негативное изображение.

function highlight(imageName) {
document.images[imageName].src =
negativelmageFile(imageName);

// Данная функция связывается с атрибутом onMouseOut;


// восстанавливает нормальное изображение курсора.

function unHighlight(imageName) {
document.images[imageName].src = regularlmageFile(imageName);

// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE">

<TABLE BORDER=0 WIDTH=150 BGCOLOR="WHITE"


CELLPADDING=0 CELLSPACING=0>
<TR><TD><A HREF="Home.html"
TARGET="Main"
onMouseOver="highlight('Home')"
onMouseOut="unHighlight('Home')">
<IMG SRC="images/Home.gif"
NAME="Home"
WIDTH=150 HEIGHT=25 BORDER=0>
</A>
<TR><TD><A HREF="Tibet.html"
TARGET="Main"
onMouseOver="highlight('Tibet')"
onMouseOut="unHighlight('Tibet')">
<IMG SRC="images/Tibet.gif"
, NAME="Tibet"
WIDTH=150 HEIGHT=25 BORDER=0>
</A>
<TR><TD><A HREF="Nepal.html"
TARGET="Main"
onMouseOver="highlight('Nepal')"
onMouseOut="unHighlight('Nepal')">
<IMG SRC="images/Nepal.gif"
NAME="Nepal"
2 4 . 5 . Использование JavaScript для изменения... 1127

WIDTH=150 HEIGHT=25 BORDER=0></A>


<TR><TD><A HREF="Austria.html"
TARGET="Main"
onMouseOver="highlight('Austria')"
onMouseOut="unHighlight('Austria')">
<IMG SRC="iinages/Austria.gif"
NAME="Austria"
WIDTH=150 HEIGHT=25 BORDER=0></A>
<TR><TD><A HREF="Switzerland.html"
TARGET="Main"
onMouseOver="highlight('Switzerland')"
onMouseOut="unHighlight('Switzerland')">
<IMG SRC="images/Switzerland.gif"
NAME="Switzerland"
WIDTH=150 HEIGHT=25 BORDER=0></A>
</TABLE>
</BODY>
</HTML>

Листинг 24.10. HighPeaks. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">


<HTML>
<HEAD>
<TITLE>High Peaks Travel Inc.</TITLE>
</HEAD>
<FRAMESET COLS="160,*" FRAMEBORDER=0 BORDER=0>
<FRAME SRC="HighPeaksNavBar.html" SCROLLING="NO">
<FRAME SRC="HighPeaksIntro.html" NAME="Main">

<NOFRAMES>
If you can't hack frames, how do you expect
to handle the Himalayas? Get a real browser.
</NOFRAMES>
</FRAMESET>
</HTML>

Листинг 24.11. HighPeaksIntro. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>High Peaks Travel Inc.</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<CENTER>
<IMG SRC="images/peak2.gif" WIDTH=511 HEIGHT=128>
</CENTER>
<H1 ALIGN="CENTER">High Peaks Travel Inc.</Hl>
<HR>
1128 Глава 24. JavaScript...

<IMG SRC="images/peakl.gif" WIDTH=170 HEIGHT=121 ALIGN="RIGHT">


Tired of the same old vacations in Cleveland?
Tour the high peaks with <B>High Peaks Travel</B>!
<P>
We have package deals for beginner, experienced, and expert
climbers, discount priced (*) for the budget-conscious
traveller.
<BR CLEAR="ALL">
<IMG SRC="images/peak.jpg" WIDTH=320 HEIGHT=240
ALIGN="LEFT">
HPT is currently arranging trips to the following
exciting locations:
<UL>
<LI><A HREF="Tibet.html">Tibet</A>
<LI><A HREF="Nepal.html">Nepal</A>
<LI><A HREF="Austria.html">Austria</A>
<LI><A HREF="Switzerland.html">Switzerland</A>
</UL>
Sign up today!
<BR CLEAR="ALL">
<CENTER>
<FONT SIZE="-2">(*) No ropes or safety equipment provided
on discount tours. </FONT>
</CENTER>
</BODY>
</HTML>

Работа CO слоями
в Netscape 4.0 был реализован механизм слоев. Слои— это области HTML-до­
кумента, которые могут перекрываться и отображаться один поверх другого. Под­
робно о слоях см. в разделе 5.12. Для работы со слоями в JavaScript 1.2 можно исполь­
зовать массив d o c u m e n t . l a y e r s , каждый элемент которого представляет собой объ­
ект L a y e r . Свойства данного объекта соответствуют атрибутам элемента LAYER. Дос­
туп к именованному слою осуществляется посредством выражения d o c u m e n t .
l a y e r s [ " и м я с л о я " ] ; такой подход предпочтительнее использования числового
индекса или выражения d o c u m e n t . и м я _ с л о я . П р и этом не имеет значения, каким
образом слои определяются в HTML: посредством элемента LAYER, с помощью эле­
мента ILAYER либо средствами листов стилей.
В листинге 24.12 представлен код примера, в котором используются слои. Перво­
начально два слоя, используемых для аннотации изображения, скрыты (рис. 24.17).
После щелчка на одной из кнопок первый слой становится видимым, отображается в
верхнем левом углу изображения, затем перемещается в требуемую точку (рис. 24.18).
После щелчка на второй кнопке первый слой перестает быть видимым, а вместо него
выводится изображение второго слоя, которое также перемещается в конечную точ­
ку. Свойства и методы объекта L a y e r описаны в разделе 25.15; в данном случае нас
интересуют свойства v i s i b i l i t y (значение show или h i d d e n ) и радеХ (абсолютная
позиция в окне). Из методов в рассматриваемом примере используются m o v e T o -
A b s o l u t e (перемещение слоя в абсолютную позицию) и moveBy (смещение слоя от­
носительно его предыдущей позиции).
24.5. Использование JavaScript для изменения. 1129

NEPAL
jueniu
swrrzERum
High Peaks Travel Inc.
Tired of the same old vacations in Cleveland? Tour
the high peaks with High Peaks Travel!

We have package deals for beginner, experienced,


and cxpett climbers, discount priced (*) for Ле
budget-conscious traveller
HPT is currently arranging
tnps to the following exciting
locations:

• Tibet
• Nepal
• Austna
• Switzerland

Sign up todayl

(*D Ho тори or <d«(y *qaji«url pcovidtd on d i f c o w i t o m

|^1;;|ьГ^ ^ W ^ B B ^ . g P Ш. ,v^„

Рис. 24.16. Если поместить курсор мыши на элемент панели инстру­


ментов, этот элемент "подсвечивается"
Данный пример может выполняться только под управлением броузера Netscape 4;
элемент LAYERS не соответствует спецификации HTML 4.0 и не поддерживается бро­
узером Netscape 6. В Internet Explorer для доступа к слоям и для их перемещения ис­
пользуется отдельная объектная модель документа (DOM). Интерактивная версия
данного примера, рассчитанная на выполнение в различных броузерах, находится на
узле h t t p : / / w w w . c o r e w e b p r o g r a r n m i n g . c o m / . И н ф о р м а ц и ю о том, как можно мо­
дифицировать документ, содержащий элемент LAYER, чтобы он выполнялся в
Netscape 6 и Internet Explorer, вы найдете по адресу h t t p : / / s i t e s . n e t s c a p e ,
net/ekrock/standards.html.

Листинг 2 4 . 1 2 . Camps, htmX

<!DOCTYPE HTML PUBLIC //W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Camps on K-3</TITLE>
<SCRIPT TYPE="text/javascript">
1130 Глава 24. JavaScript.

function hideCamps() {
// Модель документа Netscape 4.
document.layers["baseCamp"].visibility = "hidden";
document.layers["highCamp"].visibility = "hidden";
// Или document.baseCamp.visibility = "hidden";
}
function moveBaseCamp() {
baseCamp.moveBy(1, 3) ;
if (baseCamp.pageX < 130) {
setTimeout("moveBaseCamp{)", 10);
}

// "Спрятать" информацию, разместить слой в верхнем


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

function showBaseCamp() {
hideCamps();
baseCamp = document.layers["baseCamp"];
baseCamp.moveToAbsolute(0, 20);
baseCamp.visibility = "show";
moveBaseCamp();
}
function moveHighCamp() {
highCamp.moveBy(2, 1 ) ;
if (highCamp.pageX < 110) {
setTimeout("moveHighCamp()", 10);
}
}

// "Спрятать" информацию, разместить слой, содержащий


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

function showHighCamp() {
hideCamps();
highCamp = document.layers["highCamp"];
highCamp.moveToAbsolute(0, 65);
highCamp.visibility = "show";
moveHighCamp();
}
// — >
</SCRIPT>
</HEAD>
<BODY>

<IMG SRC="images/peak4.gif" WIDTH=511 HEIGHT=600 ALIGN="LEFT">


<Hl>Camps on K-3</Hl>
The High Peaks Tours trip to the summit:
<UL>
<LI>Day 1: Travel to Base Camp
<LI>Day 2: Climb to High Camp
24.6. Использование JavaScript для проверки HTML-форм 1131

<LI>Day 3: Ascend summit, return to High Camp


<LI>Day 4: Descend to Base Camp
<LI>Day 5: Return Home
</UL>
<BR CLEAR="ALL">

<!— LAYER only supported Netscape 4 —>


<LAYER ID="highCamp" PAGEX=50 PAGEY=100 VISIBILITY="hidden">
<TABLE>
<TR><TH BGCOLOR="WHITE" WIDTH=50>
<FONT SIZE="+2">High Camp</FONT>
<TD><IMG SRC="images/Arrow-Right.gif">
</TABLE>
</LAYER>

<!-- Элемент LAYER поддерживается только в Netscape 4 -->


<LAYER ID="baseCamp" PAGEX=50 PAGEY=100 VISIBILITY="hidden">
<TABLE>
<TR><TH BGCOLOR="WHITE" WIDTH=50>
<FONT SIZE="+3">Base Camp</FONT>
<TD><IMG SRC="images/Arrow-Right.gif">
</TABLE>
</LAYER>

<FORM>
<INPUT TYPE="Button" VALUE="Show Base Camp"
onClick="showBaseCamp()">
<INPUT TYPE="Button" VALUE="Show High Camp"
onClick="showHighCamp()">
<INPUT TYPE="Button" VALUE="Hide Camps"
onClick="hideCamps()">
</FORM>
</BODY>
</HTML>

24.6. Использование JavaScript для проверки


HTML-форм
Одним из важных применений JavaScript-сценариев является проверка формата
данных, введенных посредством форм, перед передачей их на сервер. Для установле­
ния соединения с сервером требуется определенное время, тем большее, чем ниже
пропускная способность линий связи, поэтому проверять, все ли поля ф о р м ы запол­
нены и не содержится ли текст в поле, предназначенном для ввода ц и ф р , лучше всего
на клиент-машине. Свойство d o c u m e n t . f o r m s содержит массив элементов Form, на­
ходящихся в составе документа. Как обычно, именованные элементы массива доступ­
ны по именам, более того, именованные ф о р м ы автоматически оформляются как
свойства объекта d o c u m e n t , поэтому для обращения к форме может использоваться
любой из приведенных ниже форматов.
1132 Глава 24. JavaScript.

var f i r s t F o r m = document.forms[0];
/ / Предполагается <FORM NAME="orders" , . . >
var orderForm = d o c u m e n t . f o r m s [ " o r d e r s " ] ;
/ / Предполагается <FORM NA]yiE="register" . . . >
var r e g i s t r a t i o n F o r m = d o c u m e n t . r e g i s t e r ;
Объект Form содержит свойство elements, которое обеспечивает доступ к масси­
ву объектов Element. Извлекать элементы из массива можно по номеру или по имени,
кроме того, можно использовать для этой цели имя свойства.
var f i r s t E l e m e n t = f i r s t F o r m . e l e m e n t s [ 0 ] ;
/ / Предполагается <INPUT . . . NAME="quantity">
var q u a n t i t y F i e l d = o r d e r F o r m . e l e m e n t s [ " q u a n t i t y " ] ;
/ / Предполагается <INPUT . . . NAME="submitSchedule">
var submitButton = r e g i s t e r . s u b m i t S c h e d u l e ;

BHMBHMHMMMHMHHDSSl

1 >ji«? a A ^ '^i c*rfa a d


Camps on K-3
The High Peaks Tours trip to the
A ЛшП summit: 1

• Day 1: Travel to Base Camp 1


I • Day 2: Climb to High Camp 1
L • Day 3; Ascend summit, 1
^LA return to Hig^ Camp 1
Н^Ц • Day 4: Descend to Base 1
^^^H| Camp 1
r^^^l • Day 5: Return Home 1

Ш ^

\уг/Ш/гг/А
, Show 8as« Camp |-р#|Ь1?'ЙдЬСз1тр | HHeC^mps |
J
Ш¥ШШ^ ;Docw»*«tDom
'' - "-^^''feaaBtJu^Lxa-^^^,-' 4
Рис. 24.17. После того как страница загружается в Netscape 4.7, оба слоя скрыты
24.6. Использование JavaScript для проверки HTML-форм 1133

:4<:'^^UitIlS'^Tl'^Z^nj

Camps on К-3
The High Peaks Tours trip to the
summit;

• Day 1: Travel to Base Camp


• Day 2: Climb to High Camp
• Day 3; Ascend summit,
return to H J ^ Camp
• Day 4: Descend to Base
Camp
• Day 5: Return Home

« Й : 1 И В ^ 1 1 ШЩЙШШ^ т» ошт:
Ш^Ш^Ё
Рис. 24.18. После щелчка на кнопке Show Base Camp первый слой отображается
в левом верхнем углу страницы, а затем перемещается в конечную позицию

Элементы формы можно обрабатывать различными способами. Чаще всего разра­


ботчики проверяют поля формы перед передачей данных на сервер (для этого ис­
пользуется атрибут onSubmit элемента FORM). Кроме того, JavaScript позволяет про­
верять содержимое конкретного итерфейсного элемента при получении и потере
фокуса ввода (атрибуты onFocus и onBlur), а также при изменении находящихся в
нем данных (атрибут onChange). Ниже подробно описаны два основных подхода к
проверке данных формы: индивидуальная обработка (проверка проводится каждый
раз, когда данные в составе элемента изменяются) и общая обработка (проверка осу­
ществляется перед передачей данных формы на сервер). Дополнительные сведения
об объектах Element и Form вы найдете в разделах 25.6 и 25.8.

Индивидуальная обработка элементов формы


в листинге 24.13 представлена простая форма, содержащая поле редактирования
и кнопку SUBMIT. При получении полем редактирования фокуса ввода в строке со­
стояния отображается текст, описывающий ожидаемые значения. При потере эле­
ментом фокуса ввода строка состояния очищается. Если пользователь вводит в поле
1134 Глава 24. JavaScript...

редактирования недопустимое значение, то по окончании ввода отображается окно с


предупреждающим сообщением. После этого поле редактирования очищается (при
изменении содержимого поля программой событие o n C h a n g e не генерируется) и по­
лучает фокус ввода для того, чтобы пользователь мог ввести корректные данные. Ре­
зультаты выполнения сценария показаны на рис. 24.19.

Листинг 2 4 . 1 3 . C h e e k T e x t . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>On-Line T r a i n i n g < / T I T L E >

<SCRIPT TYPE="text/javascript">
<! —

/ / Вывод описания в с т р о к е состояния.

function describeLanguage() {
s t a t u s = " E n t e r an i m p o r t a n t Web l a n g u a g e " ;
}

/ / Очистка строки состояния.


function c l e a r S t a t u s O {
s t a t u s = "";
}

// Когда пользователь изменяет значение поля редактирования


/ / и покидает его, выполняется проверка введенных данных.
// Если данные некорректны, выводится предупреждающее сообщение,
// введенные данные удаляются и поле редактирования снова
// получает фокус ввода.

function checkLanguage() {
var field = document.langForm.langField;
// Или document.forms["langForm"].elements["langField"]
var lang = field.value;
var prefix = lang.substring(0, 4).toUpperCase();
if (prefix != "JAVA") {
alert("Sorry, '" + lang + "* is not valid.\n" +
"Please try again.");
field.value = ""; // Erase old value
field.focus 0 ; // Give keyboard focus
}
}

// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>On-Line Training</Hl>
<FORM ACTION="cgi-bin/registerLanguage" NAME="langForm">
To see an introduction to any of our on-line training
24.6. Использование JavaScript для проверки HTML-форм 113

c o u r s e s , p l e a s e e n t e r t h e name of an i m p o r t a n t Web
programming l a n g u a g e below.
<P>
<B>Language:</B>
<INPUT TYPE="TEXT" NAME="langField"
onFocus="describeLanguage()"
onBlur="clearStatus() "
onChange="checkLanguage()">
<P>
<INPUT TYPE="SUBMIT" VALUE="Show It To Me">
</FORM>

</BODY>
</HTML>

М'||»||П1Н1|||||«ИШ
Jji
i ^ ^ ^ ^ ^ a t a rfa
On-Line Training
To see an introduction to any of our on-line training courses, please
enter the name of an important Web programming language below.

Language: | v i s u a l B a s i c

л'Show IITo Me J
BBS
ei^i' ^EO«»t«ft«P»,;J,^j|^,

Рис. 24,19. Если пользователь вводит недопусти­


мое значение, отображается окно с предупрежда­
ющим сообщением и поле редактирования снова
получает фокус

Проверка данных перед передачей на сервер


в некоторых случаях удобнее проверять сразу все данные, введенные посредством
ф о р м ы , перед передачей их на сервер. Многие пользователи считают, что отобра­
жать сообщение после заполнения каждого поля нецелесообразно, так как они пред­
почитают ввести все данные, а затем еще раз проверить их перед тем, как щелкнуть
на кнопке SUBMIT. Кроме того, проверить набор значений проще, чем создавать от­
дельную функцию для каждого интерфейсного элемента, число которых может дос­
тигать нескольких десятков. Если код, заданный посредством атрибута on S u b m i t
элемента FORM, возвращает значение f a l s e , данные на сервер не передаются.
Одной из задач, с которыми разработчики часто встречаются, организуя контроль
данных формы, является проверка числовых значений. Чтобы упростить эту провер­
ку, в JavaScript предусмотрены две встроенные функции— p a r s e i n t и p a r s e F l o a t .
П р и вызове этих функций им передаются строки символов, а возвращают они соот-
1136 Глава 24. JavaScript...

ветственно целое число и число с плавающей точкой. В JavaScript, если начало стро­
ки, предаваемой в качестве параметра, не является целым числом, функция возвра­
щает значение NaN (Not а Number— не число), которое распознается встроенной
функцией isNaN (опреатор == применять нельзя, поскольку выражение NaN==NaN
возвращает значение f a l s e ) . Как было сказано в разделе 24.1, JavaScript не поддер­
живает свойство, которое сообщало бы текущую версию языка.
Код, представленный в листинге 24.14, иллюстрирует проверку на наличие число­
вых значений. Результаты выполнения данного кода в броузере Internet Explorer 5.0
показаны на рис. 24.20.

Листинг 2 4 . 1 4 . Numbers, h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
< T I T L E > T e s t i n g Numbers</TITLE>

<SCRIPT TYPE="text/javascript">

function isint(numString) {
/ / Функция p a r s e i n t в о з в р а щ а е т либо ц е л о е ч и с л о , либо NaN
return(!isNaN(parseint(numString)));
}

/ / —>
</SCRIPT>

</HEAD>
<BODY BGCOLOR="WHITE">

<SCRIPT TYPE="text/javascript">
<! —

function testint(numString) {
return("<TR><TD>" + numString +
"<TD>" + p a r s e i n t ( n u m S t r i n g ) +
"<TD>" + i s I n t ( n u m S t r i n g ) + " X n " ) ;
}

document.writeln
( " < H l > T e s t i n g f o r Numbers i n J a v a S c r i p t 1 . 2 + < / H l > \ n " +
"<TABLE B0RDER=5 CELLSPACING=5>\n" +
"<TR><TH>Input<TH>Parsed Value<TH>Legal I n t e g e r ? \ n " +
testlntC'O") +
testint("10") +
testint("-10") +
testint("FF") +
testint("#FF") +
testint("123abc") +
testint("abcl23") +
"</TABLE>");

/ / —>
24.6. Использование JavaScript для проверки HTML-форм 1137

</SCRIPT>

</BODY>
</HTML>

ii»!ll4!i4H.i.lH
le £ЕЙ )£*M Favorte»
BOS
lock йф

Testing for Numbers in 1


JavaScript 1.1+
\ Input Parsed Value Legal Integer?
t 0 0 true 1
1 10 'lO true 1
i -10 -10 true 1
^,FF NaN false 1
''#FF NaN false
123abc 123 true 1
^ abcl23 NaN false

Рис. 24.20. Если в начале строки не содержится


iUJ^N««' "r^iaf%»^™- число, метод p a r s e i n t возвращает значение NaN

Версии JavaScript, предшествующие версии 1.1, не содержат метода isNaN. Одна­


ко, если число, вводимое в поле редактирования, должно быть больше нуля, проверку
можно осуществить очень просто, используя тот факт, что при сравнении NaN с лю­
бым числом возвращается значение f a l s e . Ниже приведен фрагмент кода, в котором
используется данный подход.
function isint(string) {
var val = parseint(string);
return(val > 0 ) ;
}
В листинге 24.15 показан документ, содержащий форму ввода, в составе которой
находятся три поля редактирования. Первое и третье поля предназначены для ввода
чисел, а во втором поле пользователь должен ввести строку текста. Вместо индивиду­
ального контроля каждого из вводимых значений единственное действие, которое
выполняет сценарий до щелчка на кнопке SUBMIT, — это вывод сообщения в строке
состояния. Сообщение отображается при получении фокуса ввода полем редактиро­
вания. Когда пользователь предпринимает попытку передать данные на сервер, вы­
зывается функция c h e c k R e g i s t r a t i o n . Эта функция проверяет, введены ли в пер­
вом и третьем поле редактирования целые числа, и позволяет убедиться, что второе
поле не осталось пустым и в нем не содержится число. Результаты показаны на
рис. 24.21 и 24.22.
1138 Глава 24. JavaScript...

El
Листинг 24.15. CheckSeveral. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<-^ЕАП>
<T .'"LE>Camp Registration</TITLE>

<SCRIPT TYPE="text/javascript">
<! —

function clearStatusO { status = ""; }

function promptAge() { status = "Age (no fractions)"; }

function promptRankO { status = "Rank Name"; }

function promptSerial0 { status = "Serial Number"; }

// В JavaScript 1.1+, parseint для параметров, не являющихся


// целыми числами, возвращает значение NaN. Это значение
// распознается посредством функции isNaN(). В JavaScript 1.0
// возвращается значение О, функция isNaN отсутствует.
// Поскольку при сравнении с NaN всегда возвращается
// значение false, проверка "> О" работает в любой версии.
function isint(string) {
var val = parseint(string);
return(val > 0 ) ;

// Проверки:
// 1) В поле Age задано целое число.
/ / 2 ) В поле Rank введено нечисловое значение.
// 3) Значение в поле Rank присутствует.
// 4) В поле Serial number задано целое число.
// Если хотя бы одна проверка дает отрицательный результат,
// данные на сервер не передаются.

function checkRegistration() {
var ageField = document.registerForm.ageField;
if (!isInt(ageField.value)) {
alert("Age must be an integer.");
return(false);
}
var rankField = document.registerForm.rankField;
if (isInt(rankField.value)) {
alert("Use rank name, not rank number.");
return(false);
}
if (rankField.value == "") {
alert("Missing rank.");
return(false);
}
var serialField = document.registerForm.serialField;
if (!isInt(serialField.value)) {
24.6. Использование JavaScript для проверки HTML-форм 1139

alert("Serial number must be an integer.");


return(false);
}
// Ошибок в формате данных не обнаружено. Данные формы
// передаются на сервер.
return(true);

// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>Camp Registration</Hl>
<FORM ACTION="cgi-bin/register"
NAME="registerForm"
onSubmit="return(checkRegistration())">
Age: <INPUT TYPE="TEXT" NAME="ageField"
onFocus="promptAge()"
onBlur="clearStatus()">
<BR>
Rank: <INPUT TYPE="TEXT" NAME="rankField"
onFocus="promptRank()"
onBlur="clearStatus()">
<BR>
Serial Number: <INPUT TYPE="TEXT" NAME="serialField"
onFocus="promptSerial()"
onBlur="clearStatus()">
<P>
<INPUT TYPE="SUBMIT" VALUE="Submit Registration">
</FORM>
</BODY>
</HTML>

Camp Registration
Agcijie"
Rank:}Geek
Senal Number |#FFAA|

!^^pf ~Bi^m(>iiM:^j^^.j^:^m.^^<j

Рис. 24.21. В процессе ввода данных про­


верка не выполняется.

Рис. 24.22. Увы, разработчик


сценария не предусмотрел
возможность ввода шестнад-
цатеричных чисел
1140 Глава 2 4 . JavaScript.

Помните, что броузер Netscape, поддерживающий JavaScript 1.2, не выполняет ав­


томатическое приведение типов для операндов операторов == и ! =.
" 1 2 3 " == 123 / / Возвращает з н а ч е н и е f a l s e . Только для J a v a S c r i p t 1 . 2 !
" 7 7 7 " != 777 / / Возвращает з н а ч е н и е t r u e . Только для J a v a S c r i p t 1 . 2 !
В остальных версиях JavaScript приведение типов выполняется автоматически.
Internet Explorer выполняет приведение типов во всех реализациях ECMAScript, по­
этому пользователи данного броузера не страдают от несоответствия, имеющего ме­
сто в JavaScript 1.2.
На з а м е т к у

В JavaScript 1.2 Netscape не выполняет приведение типов для опе­


рандов операторов == и !=. Такое поведение характерно только для
JavaScript 1.2.

24.7. Использование JavaScript для хранения


и проверки cookie
Запись cookie — это небольшой фрагмент текстовых данных, содержащий инфор­
мацию о Web-странице. Cookie сохраняется броузером на клиентской машине. Вся
обработка cookie может осуществляться на стороне клиента; чтобы это стало воз­
можным, JavaScript предоставляет свойство d o c u m e n t . c o o k i e . Поведение данного
свойства может показаться не совсем обычным. Обратившись к свойству d o c u m e n t ,
c o o k i e , вы получите одну текстовую строку, в которой содержатся все записи cookie,
переданные броузером в поле Cookie заголовка HTTP-запроса. Например, если для
текущей страницы определены три cookie с именами n a m e l , name2 и п а т е З , значе­
ние d o c u m e n t . c o o k i e будет выглядеть приблизительно следующим образом:
"name1=vail; name2=val2; name3=val3"
Но это совсем не означает, что все cookie должны одновременно присваиваться
свойству d o c u m e n t . c o o k i e . Записи cookie включаются в состав данного свойства
поочередно и задаются в том же формате, в котором они указываются в поле S e t -
C o o k i e заголовка НТТР-зарпоса. Подробно синтаксис записей cookie был описан в
разделе 19.1, здесь же мы ограничимся несколькими примерами.
document.cookie = "namel=vall";
d o c u m e n t . c o o k i e = "name2=val2; e x p i r e s = " + someDate;
d o c u m e n t . c o o k i e =?= " n a m e 3 = v a l 3 ; p a t h = / ; d o m a i n = t e s t . com";
Каждый раз, когда свойству d o c u m e n t . c o o k i e присваивается новое значение, за­
пись cookie сохраняется броузером. Cookie присутствуют до закрытия броузера, а при
последующем запуске сохраняются только те из них, время существования которых
еще не истекло. Так работают все версии Netscape, однако среди броузеров Internet
Explorer подобное поведение демонстрируют только 5.0 и более новые версии.

Внимание!

В Internet Expiorer 4.x и более ранних версиях записи cooicie, полу­


ченные из локального файла, игнорируются.
24.6. Использование JavaScript для проверки HTML-форм 1141

Использование cookie демонстрирует простая форма заказа корпорации "Widgets


R Us", представленная в листинге 24.16. П р и передаче данных ф о р м ы имя, фамилия и
номер счета сохраняются посредством cookie. Пользователь может сохранить дан­
ные, не передавая заказ; для этого достаточно щелкнуть на кнопке Register Account.
Если пользователь впоследствии обратится к данной Web-странице (в текущем сеансе
либо в одном из последующих сеансов работы), значения cookie будут использованы
для автоматического заполнения трех первых полей формы.
Рассматривая данный пример, обратите внимание на две особенности. Во-первых,
функция, которая заполняет поля редактирования, указана как значение атрибута
onLoad элемента BODY. Такой подход гарантирует, что функция будет вызвана уже по­
сле загрузки Web-страницы и попытка заполнить несуществующую форму не будет
предпринята. Во-вторых, для обработки строки cookie используется функция c o o k i e -
Val. При вызове этой функции передается имя cookie (например, "name2") и строка
cookie (например, " n a m e l = v a l l ; n a m e 2 = v a l 2 ; n a m e 3 = v a l 3 " ) . В результате выпол­
нения функция возвращает значение, связанное с указанным именем (т.е. " v a l 2 " ) .
function cookieVal(cookieName, cookieString) {
var startLoc = cookieString.indexOf(cookieName);
i f ( S t a r t L o c == - 1 ) {
returnC'"); / / Запись c o o k i e о т с у т с т в у е т .
}
v a r sepLoc = cookieString.indexOf{"=", startLoc);
v a r endLoc = cookieString.indexOf(";", startLoc);
i f (endLoc == - 1 ) { / / Строка не содержит " ; " .
endLoc = cookieString.length;
}
return(cookieString.substring(sepLoc+1, endLoc));
}

В данной функции используются два важных метода объекта S t r i n g : i n d e x O f и


s u b s t r i n g . Методу i n d e x O f передаются в качестве параметров две строки. Если
первая строка входит в состав второй, метод возвращает индекс первого вхождения
строки. Кроме i n d e x O f , в классе S t r i n g определен метод l a s t l n d e x O f , который
возвращает индекс последнего вхождения первой строки во втор)то. Если первая
строка не является частью второй, оба этих метода возвращают значение -1. Методу
s u b s t r i n g передаются начальный и конечный индексы; в результате выполнения
этого метода возвращается подстрока, определяемая параметрами. В состав подстро­
ки входит символ, на который указывает начальный индекс, и не входит символ, оп­
ределяемый конечным индексом. Функция c o o k i e V a l могла бы быть несколько про­
ще, если бы в ней использовался метод s p l i t . Этот метод разбивает заданную строку
на подстроки; разделителем считается пробел или другой символ, заданный пользо­
вателем. Объект S t r i n g подробно описан в разделе 25.31.
Приведенный здесь вариант функции c o o k i e V a l одинаково обрабатывает пустое
значение cookie (например, значение, соответствующее имени " b a r " ) в строке
" f o o = a ; b a r = ; b a z = c " ) и отсутствующую запись (например, значение, соответст­
вующее имени "quux" в строке " f o o = a ; b a r = ; b a z = c " ) . Если вы хотите, чтобы про­
грамма реагировала на данные ситуации по-разному, функция c o o k i e V a l вместо пус­
той строки должна возвращать значение n u l l ; для этого надо переписать строку, по­
меченную комментариями "Запись cookie отсутствует". В данном примере в обоих
случаях возвращается пустая строка, что упрощает процесс обработки.
1142 Глава 24. JavaScript...

На рис. 24.23 и 24.24 показаны результаты выполнения данного сценария в среде


броузера Internet Explorer 5.0. Отладка взаимодействия с использованием cookie мо­
жет вызывать значительные трудности. Для того чтобы выяснить, присутствуют ли
cookie на текущей странице, надо ввести в поле броузера, предназначенного для вво­
да URL, следующую строку:
javascript:alert(document.cookie)

Листинг 24.16. Widgets. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Widgets "R" Us</TITLE>
<SCRIPT TYPE="text/javascript">
<! —
// Чтение имени, фамилии и номера счета, заданных в полях
// редактирования, и сохранение их с помощью cookie.
function storeCookies() {
var expires = "; expires=Monday, 01-Dec-Ol 23:59:59 GMT";
var first = document.widgetForm.firstField.value;
var last = document.widgetForm.lastField.value;
var account = document.widgetForm.accountField.value;
document.cookie = "first=" + first + expires;
document.cookie = "last=" + last + expires;
document.cookie = "account=" + account + expires;
}
// Сохранение cookie и получение подтверждения пользователя.
function registerAccount() {
StoreCookies();
alert("Registration Successful.");
}
// Пустая запись cookie и отсутствие cookie
// обрабатываются одинаково.

function cookieVal(cookieName, cookieString) {


var startLoc = cookieString.indexOf(cookieName);
if (StartLoc == -1) {
return(""); // Запись cookie отсутствует.
}
var sepLoc = cookieString.indexOf("=", startLoc);
var endLoc = cookieString.indexOf(";", startLoc);
if (endLoc == -1) { // Строка не содержит ";".
endLoc = cookieString.length;
}
return(cookieString.substring(sepLoc+1, endLoc));
}
// Если существует запись cookie для имени и номера счета,
// соответствующие поля редактирования заполняются.
function presetValues() {
24.6. Использование JavaScript для проверки HTML-форм 1143

var firstField = document.widgetForm.firstField;


var lastField = document.widgetForm.lastField;
var accountField = document.widgetForm.accountField;
var cookies = document.cookie;
firstField.value = cookieVal("first", cookies);
lastField.value = cookieVal("last", cookies);
accountField.value = cookieVal("account", cookies);
}
// ~ >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE" onLoad="presetValues()">
<Hl>Widgets "R" Us</Hl>
<FORM ACTION="servlet/cwp.Widgets"
NAME="widgetForm"
onSubmit="storeCookies()">
First Name: <INPUT TYPE="TEXT" NAME="firstField">
<BR>
Last Name: <INPUT TYPE="TEXT" NAME="lastField">
<BR>
Account Number: <INPUT TYPE="TEXT" NAME="accountField">
<BR>
Widget Name: <INPUT TYPE="TEXT" NAME="widgetField">
<BR>
<INPUT TYPE="BUTTON" VALUE="Register Account"
onClick="registerAccount()">
<INPUT TYPE="SUBMIT" VALUE="Submit Order">
</FORM>
</BODY>
</HTML>

1^ vBrffl№iHMii»jiBmiffl3»r Ш ^ шшштшршшшшшмщ
~}
Widgets "R" Us Widgets "R" Us
First Name: jLoreen 1 First Name: JLoreen
Last Name: iDeWitt 1 Last Name: JDeWitt
Account Number |1100-BC 1 Account Number |1100-BC
Widget Name jAcme-Ultra-Widget 1 Widget Name' j
SubmrtOrdet 1 l4eg»9tasrA<xooot j Submit Ofdef j

d
,€3Done [ЩЩ^ёт^а' '{gjOone I ;Д(%Ь(Ш]М"

Рис. 24.23. Эта страница предназ­ Рис. 24.24. Даже если пользователь
начена для передачи заказа. При пе­ Loreen завершит работу с броузе­
редаче данных на сервер или после ром, а затем повторно обратится к
щелчка на кнопке Register Account Web-странице, некоторые поля фор­
имя и номер счета сохраняются мы будут автоматически заполнены
1144 Глава 24. JavaScript.

24.8. Использование JavaScript для работы


с фреймами
JavaScript предоставляет различные средства для работы с фреймами. Свойство
f r a m e s объекта Window обеспечивает доступ к массиву, состоящему из фреймов
(других объектов Window), которые входят в состав текущего окна или фрейма. Свой­
ства p a r e n t и t o p ссылаются соответственно на родительский фрейм или окно, и на
окно верхнего уровня. Подробно объект Window и его свойства будут описаны в раз­
деле 25.35. В данном разделе приведены два примера, показывающие, как из одного
фрейма задается отображение ресурса с заданным URL в другом ф р е й м е и как при
изменении содержимого ф р е й м а ему передается фокус. Кроме того, два примера, де­
монстрирующие одновременное обновление содержимого двух фреймов и запрет
отображения документа в виде фрейма, были представлены в главе 4.

Отображение во фрейме ресурса с заданным URL


Каждый фрейм содержит свойство l o c a t i o n , при изменении значения которого
во фрейме отображается новый ресурс. Поскольку ф р е й м ы описываются посредст­
вом массива f r a m e s , не составляет труда задать из JavaScript-сценария отображение
ресурса в требуемом фрейме. Имея ссылку на родительский фрейм, можно посредст­
вом массива f r a m e найти требуемый фрейм, обратиться к нему и изменить его свой­
ство l o c a t i o n . П р и м е р выполнения подобного действия приведен ниже.
someFrame.frames["frameName"].location = "url";

В объектной модели HTML для каждого фрейма в окне создается отдельное свой­
ство, поэтому тот же результат будет получен с помощью следующего выражения:
someFrame.frames.frameName.location = "url";

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


В противном случае вы не сможете правильно следовать по ссылкам p a r e n t , t o p и
f r a m e s и находить требуемый фрейм. Кроме того, в процессе выполнения сценария
есть возможность создавать новые окна и отображать в них документы. Соответст­
вующие примеры приведены в разделе 25.35.
В листинге 24.17 показан HTML-код документа, который первоначально содержит
два фрейма: G e t U R L . h t m l (листинг 24.18) и D i s p l a y U R L . h t m l (листинг 24.19). Во
фрейме, расположенном в верхней части окна, находится поле редактирования для
ввода URL. После указания требуемого значения и щелчка на кнопке Show URL сце­
нарий задает вывод документа в другом фрейме. Код, выполняющий описанные дей­
ствия, достаточно прост. В первую очередь надо создать функцию, предназначенную
для отображения ресурса:
f u n c t i o n showURLO {
v a r u r l = document . u r l F o r m . u r l F i e l d . valued-
parent. displayFrame . l o c a t i o n = u r l ;
}
24.8. Использование JavaScript для работы с фреймами 1145

Затем эту функцию надо связать с кнопкой, используя для этого атрибут o n C l i c k :

<INPUT TYPE="BUTTON" VALUE="Show URL"


onClick="showURL()">

Ha рис. 24.25 и 24.26 показаны результаты, отображаемые в окне броузера Internet


Explorer.

Листинг 2 4 . 1 7 . ShowURL.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Frameset//EN">


<HTML>
<HEAD>
<TITLE>Show a URL</TITLE>
</HEAD>

<FRAMESET ROWS="150, *">


<FRAME SRC="GetURL.html" NAME="inputFrame">
<FRAME SRC="DisplayURL.html" NAME="displayFrame">
</FRAMESET>

</HTML>

Листинг 24.18. GetURL.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Choose a URL</TITLE>

<SCRIPT TYPE="text/javascript">
<! —

function showURL() {
var url = document .urlForm.urlField. valued-
parent .displayFrame. location = url;
// Или parent.frames["displayFrame"]•location = url;
}

function preloadUrlO {
if (navigator.appName == "Netscape") {
document.urlForm.urlField.value =
"http://home.netscape.com/";
} else {
document.urlForm.urlField.value =
"http://www.microsoft.com/";
}
}

// — >
</SCRIPT>
</HEAD>
1146 Глава 24. JavaScript...

<BODY BGCOLOR="WHITE" onLoad="preloadUrl()">


<H1 ALIGN="CENTER">Choose a URL</H1>

<CENTER>
<FORM N A M E = " u r l F o r m " >
URL: <INPUT TYPE="TEXT" N A M E = " u r l F i e l d " SIZE=35>
<INPUT TYPE="BUTTON" VALUE="Show URL"
onClick="showURL()">
</FORM>
</CENTER>

</BODY>
</HTML>

Листинг 2 4 . 1 9 . DisplayURL. html

<!DOCTYPE HTML PUBLIC " - / / W 3 C / / D T D HTML 4 . 0 T r a n s i t i o n a l / / E N " >


<HTML>
<HEAD> -
< T I T L E > D i s p l a y URL</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
< H 2 > E n t e r a URL i n t h e t e x t f i e l d a b o v e . P r e s s
" S h o w URL" t o d i s p l a y i t i n t h i s frame.</H2>

</BODY>
</HTML>

HQQj^l
iCTBirewRrai—
s.^^a^^ amm
'4Eh- £'^# ^*
)imt go ^«мптк^ог \Ыр
ш\
Choose a URL
XJRL: | h t t p : / / h o m e . n e t s c a p e . com/
—r- J

1 Enter a URL in the textfleld above. Press "Show URL" to display it in this
j frame.

ШШ1 " lOooumfHt.Pfirte J4


Рис. 24.25. В поле редактирования введен адрес Netscape, однако пользователь
может задать произвольное значение
24.8. Использование JavaScript для работы с фреймами 1147

^ SIHMW а URL • Netscape

п ^ ^' -a Л A ^ ^ rf's «
Choose a URL
XJRL" [http://hoine.netscape.com/ SH^wURl I

Scree_n_J<Ln9s Tax..Tjrnfi HOT D t A L S « M


Kurt Russell ^nd Kevin ThetaH deadline is fast • M u s t - H a v e s ! J.Crew
Costner lead a gang of approaching. Consult our Spring 2 0 0 1 for M e n
Elvis impersonators on comprehensive tan guide • Victoria's S e c r e t ; Sv
heist in ' 3 0 0 0 Miles to ill the resources you 2 0 0 1 Collection
Graceland,' Also debuting need to file on time. • Swfully Delicious;
this weekend is Brendan N^tsc^pe Tax Guide EntfirtainniEnt G o u r m e t Body Peint
Frasier in 'Monkeybone.'
O y t n n q thii Weekend FarcwGit 'Friends'? 4VLftrHtR
NBC's sitcom flagship is
sinking - IS it tinne to I EH •
put us out of Its m i s e r y ' Enter Zip Code or City §^|

.i^H>'l r:^^. Ш ,я>

Рис. 24.26. После щелчка на кнопке Show URL в нижнем фрейме отображается
документ, URL которого задан в поле редактирования, находящемся в верхнем
фрейме. (Приведено с разрешения Netscape Communications Corp.)

Получение фреймом фокуса ввода


Web-страницы, содержащие ф р е й м ы , имеют одну важную особенность. Если вы
активизируете гипертекстовую ссылку, по которой документ выводится в другом
фрейме, этот ф р е й м не получает автоматически фокус ввода. В некоторых случаях
это становится источником недоразумений. Во-первых, при печати могут возникнуть
неожиданные результаты. Предположим, что на вашей странице находится фрейм
без обрамления, в составе которого содержится набор кнопок. Щелкнув на одной из
кнопок, вы загружаете в другой ф р е й м документ, а затем вызываете операцию печати.
Каковы будут результаты? Вы получите бумажную копию панели инструментов! Вряд
ли вы к этому стремились. Во-вторых, данная особенность может отменять действия
специальных клавиш. Часто для просмотра больших документов помимо полос про­
крутки используются клавиши со стрелками, однако это возможно только в том окне,
которое имеет фокус ввода. Поэтому, если вы активизируете гипертекстовую ссылку,
загружающую документ в другой ф р е й м , нажатие клавиш со стрелками не приведет к
требуемому результату до тех пор, пока вы не щелкнете мышью в нужном фрейме.
П р и использовании JavaScript описанная проблема разрешается очень просто —
достаточно вызвать метод f e c u s ( ) . Н и ж е приведена улучшенная версия функции
showURL, которая может быть использована в G e t U R L . h t m l для назначения фокуса
ввода нижнему фрейму после отображения в нем документа.
f u n c t i o n showURLO {
var u r l = document.urlForm.urlField.value;
parent.displayFrame.location = url;
/ / Give frame t h e i n p u t f o c u s
parent.displayFrame.focus 0 ;
}
1148 Глава 24. JavaScript.

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


надо использовать атрибут o n C l i c k в каждом элементе А и AREA, содержащем
TARGET, и атрибут o n S u b m i t в соответствующем элементе FORM. С каждым из этих
атрибутов необходимо связать вызов функции f o c u s .

24.9. Обращение к Java из JavaScript


Начиная с версии 3.0 в составе Netscape появился пакет под названием LiveConnect,
который обеспечивает взаимодействие JavaScript и Java-кода. Такая возможность оказа­
лась очень полезной; ранее Java-аплет представлял собой "замкнутую" структуру и не
имел никакой информации об остальных элементах Web-страницы. Теперь аплеты мо­
гут работать с фреймами и окнами, управлять изображениями в составе HTML-
документа, читать и устанавливать значения интерактивных элементов в составе форм,
читать и сохранять записи cookie и выполнять другие подобные действия, которые в
прошлом были доступны только JavaScript-сценариям. В данном разделе рассматрива­
ются способы использования Java-программ и управления аплетами из JavaScript-
сценариев. В разделе 24.10 будет обсуждаться использование возможностей JavaScript из
аплетов. Последующие подразделы посвящены рассмотрению таких вопросов.

• Непосредственный вызов методов Java. В частности, здесь уделяется внимание вы­


воду отладочных сообщений на Java-консоль.
• Использование аплетов для выполнения операций, необходимых для JavaScript-сценария.
В этом разделе в основном будут обсуждаться вопросы получения скрытым ап-
летом имени узла клиента (подобная информация недоступна JavaScript-
сценариям).
• Управлени£ аплетами из JavaScnpt-сценариев. В частности, мы покажем, как
LiveConnect позволяет связать фрагменты кода аплета с действиями пользова­
теля в остальной части Web-страницы.

Непосредственный вызов методов Java


Для обращения к переменным и методам Java из JavaScript-сценария достаточно
задать полностью определенное имя переменной или метода. Например, в результате
выполнения выражения
Java.lang.System.out.println{"Hello Console");
на Java-консоль будет передана строка " H e l l o C o n s o l e " . Это удобное средство от­
ладки Web-страниц, содержащих JavaScript-сценарии. В JavaScript-сценарии можно
также применять оператор new для создания экземпляров Java-классов. Например, в
листинге 24.20 представлена простая Web-страница, на которой используется метод
g e t P r o p e r t y класса J a v a . l a n g . S y s t e m и экземпляр класса j a v a . a w t . P o i n t . Ре­
зультаты показаны на рис. 24.27.
2 4 . 9 . Обращение к Java из JavaScript 1149

Листинг24.20. C a l l J a v a . h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">


<HTML>
<HEAD>
<TITLE>Calling Java</TITLE>
</HEAD>
<BODY>
<Hl>Calling Java</Hl>

<SCRIPT TYPE="text/javascript">

document.writeln
("This browser uses a virtual machine from " +
Java.lang.System.getProperty("Java.vendor") + " . " ) ;
var pt = new Java.awt.Point(3, 5 ) ;
pt.translate(7, 5 ) ;
document.writeln("<P>");
document.writeln("Translating (3,5) by (7,5) yields (" +
pt.X + "," + pt.у + " ) . " ) ;

// — >
</SCRIPT>

</BODY>
</HTML>

Calling Java
This browser uses a virtual machine from Netscape Communications Corporation.

Translating (3.5) by (7.5) yields (10,10)

Рис. 24.27. JavaScript-сценарий может использовать средства


Java даже в том случае, если в составе Web-страницы нет аплетов

На использование средств Java в JavaScript-сценарии накладываются два основных


ограничения. Во-первых, все операции, запрещенные в аплете, запрещены также и в
JavaScript-сценарии. Так, вы не сможете открыть файл на диске, вызвать локальную
программу, определить регистрационное имя пользователя и выполнить другие опе­
рации, связанные с доступом к ресурсам. Во-вторых, в JavaScript отсутствует механизм
для реализации методов и определения подклассов Java. В результате, если вам надо
выполнить более сложное действие, чем вызвать метод J a v a . l a n g . S y s t e m . o u t .
p r i n t I n или обратиться к средствам класса j a v a . u t i l . S t r i n g T o k e n i z e r , вам при­
дется создать аплет.
1150 Глава 24. JavaScript...

Использование аплетов для выполнения


операций в JavaScript-сценариях
Следует признать, что язык Java лучше подходит для создания структур данных и
выполнения сложных вычислений, чем JavaScript. Если при создании JavaScript-
сценария возникла необходимость в выполнении подобного рода задач, желательно
реализовать соответствующий Java-код, о ф о р м и т ь его в виде "скрытого" аплета и вы­
зывать соответствующие методы из JavaScript-сценария. JavaScript-код может обра­
щаться к аплетам либо посредством массива d o c u m e n t . a p p l e t s , либо, если аплету
присвоено имя, с помощью выражения d o c u m e n t . a p p l e t N a m e , где a p p l e t N a m e —
имя требуемого аплета. Из JavaScript-сценария доступны все методы аплета, объяв­
ленные как p u b l i c . Предположим, например, что в аплете A c o u s t i c s реализована
модель распространения звука в жидкой среде и вы создаете Web-страницу для демон­
страции этой модели. В этом случае надо включить аплет в состав Web-страницы сле­
дующим образом:
<APPLET CODE="Acoustics" WIDTH=10 HEIGHT=10
NAME="acoustics">
</APPLET>
После этого вы можете вызвать общедоступный метод g e t S i g n a l E x c e s s :
function signalExcess(...) {
return(document.acoustics.getSignalExcess (...));
}

В листинге 24.21 приведен код Web-страницы с гипертекстовой ссылкой, при ак­


тивизации которой отображается одно из двух резюме автора страницы. Выбор кон­
кретных данных зависит от того, какому домену принадлежит клиент. Поскольку
JavaScript не имеет возможности определить имя клиент-машины, в состав Web-
страницы включен простой скрытый аплет (рис. 24.28 и листинг 24.22), использую­
щий для определения доменного имени метод I n e t A d d r e s s . g e t L o c a l H o s t . Если
узел принадлежит локальной сети той компании, где работает автор Web-страницы,
пользователю предлагается версия резюме, полностью соответствующая политике
компании (рис. 24.29, листинг 24.23). Другие пользователи, щелкнув на той же ссыл­
ке, получат совершенно иной результат (рис. 24.30, листинг 24.24).

Листинг 2 4 . 2 1 . Wonder-Widget. h t m l

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>WonderWidget</TITLE>

<SCRIPT TYPE="text/javascript">
<! —

function c o n t a i n s ( s t r i n g , substring) {
return(string.indexOf(substring) != - 1 ) ;
}
2 4 . 9 . Обращение к Java из JavaScript 1151

function showResume() {
if (contains(document.gethost.getHost(),
"widgets-r-us.com")) {
location = "ResumeLoyal.html";
} else {
location = "ResumeReal.html";
}
return(false) ;
}

// — >
</SCRIPT>

</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>WonderWidget</Hl>

<APPLET CODE="GetHost" WIDTH=10 HEIGHT=10 NAME="gethost">


</APPLET>

Description:
<UL>
<LI>Name: Wonder Widget
<LI>Serial Number: 1544X
<LI>Cost: $7.95 (plus 22.50 shipping and handling)
<LI>Designer:
<A HREF="ResumeLoyal.html" onClick="return(showResume())">
J. Random Hacker</A>

</BODY>
</HTML>

Листинг 24.22.GetHost.java

import Java . applet .Applets-


import java.awt.*;
import java.net.*;
public class GetHost extends Applet {
private String host;
public void initO {
setBackground(Color.white);
try {
host = InetAddress.getLocalHost().toString();
} catch(UnknownHostException uhe) {
host = "Unknown Host";
}
}

public String getHost () {


return(host);
}
}
1152 Глава 24. JavaScript.

^ W o n d e t W k ^ l ^ • Netscape
Efc £cft yiew So jQocnmunicator й ф
^^ y'^ J г-л ^ !jvi ,.> f^' ti i; 1Л;

WonderWidget
Description:

• Name: WonderWidget
• Senal Number: 1544X
• Cost $7 95 (plus 22.50 shipping and handling)
• Designer J Fvandom Hacker Рис. 24.28. Данная страница
содержит аплет, однако он не
' ''--i.^ Dcxumertl, bont ;^ i^ iM -г отображается на экране

lis £dk Vtew go Qomnnfiirlicata Й Ф

: ^'^ -r 3 ^Jt ^ 5l ~> *si' Ъ Ш


Widgets R Us
J. Random Hacker has been a loyal employee of Widgets R. Us, Inc,
for five years During that time he has selflessly worked on a number of Рис. 24.29. Если ссылка " J .
projects that have greatly benefited the company. His most recent Random Hacker" активизируется
achievement is the Wonder W-dpet. клиентом из домена w i d g e t s - r - u s
или если в броузере запрещено
выполнение JavaScript-сценариев,
Apptet getl-tos* ;rtopped i:i! ^^ отображается данный текст

£йе £сЙ Vievt go CommunKatot Н Ф

J. Random Hacker
Гш looking for а job!
For the last five years, I've been undeфald and underappreciated by
Widgets R Us, Inc. Now^ I'm ready to take my immense talents' Риа. 24.30. Если, судя по
elsewhere. Who will open the bidding? доменному имени клиента, автору
Web-страницы "ничего не грозит",
:a^ ~4^>- ?\ppiet gethosi itopped ^ J - ' ZM \ ^ отображаются другие сведения

Листинг 2 4 . 2 3 . ResumeLoyal. html

<!DOCTYPE HTML PUBLIC " - / / W 3 C / / D T D HTML 4 . 0 Transitioanl//EN">


<HTML>
<HEAD>
<TITLE>Widgets R Us</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>Widgets R Us</Hl>
2 4 . 9 . Обращение к Java из JavaScript 1153

<B>J. Random Hacker</B> has been a loyal employee of Widgets


R. Us^ Inc, for five years. During that time he has
selflessly worked on a number of projects that have greatly
benefited the company. His most recent achievement is the
<A HREF="Wonder--Widget.html">Wonder Widget</A>.
</BODY>
</HTML>

Листинг 24.24. ResxjmeReal. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>J. Random Hacker</TITLE>
</HEAD>
<BODY BGCOLOR="WHITE">
<H1>J. Random Hacker</Hl>
<H2>I*m looking for a job!</H2>
For the last five years, I've been underpaid and
underappreciated by Widgets R Us, Inc. Now I'm ready to take
my immense talents elsewhere. Who will open the bidding?
</BODY>
</HTML>

Управление аплетами из иауаЗсприсценария


Если в аплете предусмотрены общедоступные методы, позволяющие запускать и
останавливать его, а также осуществлять другие действия по управлению им, работа
данного аплета может контролироваться из JavaScript-сценария. Вы спросите:
"Почему не делать т о же самое с помощью управляющих элементов аплета?". В неко­
торых ситуациях подобное р е ш е н и е оказывается неудобным и даже неприемлемым.
Во-первых, вам может потребоваться объединить управляющие элементы и форма­
тированный текст. Н а п р и м е р , если вы захотите включить и н т е р ф е й с н ы й элемент в
состав таблицы, сделать это гораздо удобнее с помощью HTML, чем средствами Java.
Во-вторых, вам, возможно, потребуется, чтобы н е к о т о р ы е ф р а г м е н т ы Java-кода вы­
полнялись в результате действий, производимых пользователем, например при акти­
визации кнопки SUBMIT ф о р м ы . Java-аплет не может распознавать подобные события.
В-третьих, использование JavaScript позволяет выполнять н е к о т о р ы е действия для
всех аплетов, входящих в состав документа. Например, в листинге 24.25 представлен
код Web-страницы, которая позволяет управлять несколькими "имитаторами", реали­
зованными в виде аплетов. П р и активизации кнопок Start и Stop действия выполня­
ются над всеми аплетами в составе документа. Результат выполнения JavaScript-
сценария показан на рис. 24.31 и 24.32.
1154 Глава 24. JavaScript..

Листинг 24.25. MoldSimulation. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Mold Propagation Simulation</TITLE>

<SCRIPT TYPE="text/javascript">

// Запуск процесса моделирования для всех аплетов,


// содержащихся в документе.

function startCircles() {
for (var i=0; i<docuinent. applets . length; i++) {
document.applets[i].startCircles() ;
}
}

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


// содержащихся в документе.

function stopCircles() {
for (var i=0; i<docuinent. applets . length; i++) {
document.applets[i].stopCircles();
}
}

// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#C0C0C0">
<Hl>Mold Propagation Simulation</Hl>

<APPLET CODE="RandomCircles.class" WIDTH=100 HEIGHT=75>


</APPLET>
<P>
<APPLET CODE="RandomCircles.class" WIDTH=300 HEIGHT=75>
</APPLET>
<P>
<APPLET CODE="RandomCircles.class" WIDTH=500 HEIGHT=75>
</APPLET>

<FORM>
<INPUT TYPE="BUTTON" VALUE="Start Simulations"
onClick="startCircles ()">
<INPUT TYPE="BUTTON" VALUE="Stop Simulations"
onClick="stopCircles ()">
</FORM>

</BODY>
</HTML>
24.9. Обращение к Java из JavaScript 1155

Листинг 24.26. RandomCircles. Java

import Java, applet .Applets-


import java.awt.*;

/** Рисование кругов случайным образом в фоновом потоке.


* Запуск и остановка процесса рисования производится по
* внешним событиям.
V
public class RandomCircles extends Applet
implements Runnable {
private boolean drawCircles = falser-
public void initO {
setBackground(Color.white);
}
public void startCircles() {
Thread t = new Thread(this);
t.start 0 ;
}
public void run() {
Color[] colors = { Color.lightGray, Color.gray,
Color.darkGray, Color.black };
int colorlndex = 0;
int X, y;
int width = getSize().width;
int height == getSize () .heights-
Graphics g = getGraphics();
drawCircles = true;
while(drawCircles) {
X = (int)Math.round(width * Math.random());
у = (int)Math.round(height * Math.random());
g.setColor(colors[colorlndex]);
colorlndex = (colorlndex + 1 ) % colors.length;
g.fillOvaKx, y, 10, 10);
pause(0.1) ;
}
}
public void stopCircles() {
drawCircles = false;
}
private void pause(double seconds) {
try {
Thread.sleep((int)(Math.round(seconds * 1000.0)));
} catch(InterruptedException ie) {}
}
1156 Глава 24. JavaScript.

^ ^ И i^BS
| Я ^ ^ 1

liiP
JfciM

k^J^yM'MW'M^'en Ш li
МшШ
llff?:]

i-liiitiliiiiee^
i#ЩriiШ:-€»lfilшs*з шт^тшш Шшт™Ш;
Рис. 24.31. Сразу после загрузки Web-страницы "имитаторы"
не работают

|ШШх||
{штшшшштшвташшштштт
р'Ш|ЩШШШШ*<ШШШШ«И^ W#'-JH
:;^oid:^^lpiag^^
;^jpi*iielifiiiiii
|:„, |;.viiiii;iiiillil:lft
и i'дГ** -^'I ' ''iiii eiisi
• • •
1 ^

1 [j Start Simulatlorts Д Stop feul^tldns j


1 _,frr«r«^ - ; ^ j - r r - -71^^^.,, ^^ ^ , , ^^ ^ _^^^^ ;;Г,Д::-1-.;>.•:, v^

:Ш!Ы
Рис. 24.32. Все три имитатора управляются с помощью
одной кнопки. Включение дополнительных аплетов не потре­
бует изменения кода Java или JavaScript
2 4 . 1 0 . Доступ к средствам JavaScript из Java 1157

2 4 . 1 0 . Доступ к средствам JavaScript из Java


LiveConnect не только дает возможность вызывать Java-методы из JavaScript-
сценариев, но также позволяет аплетам обращаться к средствам JavaScript. Класс
n e t s c a p e . j a v a s c r i p t . J S O b j e c t обеспечивает доступ ко всем JavaScript-объектам,
позволяет читать и устанавливать их свойства и вызывать их методы. Более того, ме­
тод e v a l дает возможность выполнять произвольный JavaScript-код. Использование
метода e v a l оправдано, если какие-либо действия проще выполняются с помощью
JavaScript, чем средствами Java. Ч т о б ы обеспечить вызов JavaScript из Java, надо вы­
полнить следующие действия.
1. Получить и инсталлировать класс JSObject.
2. И м п о р т и р о в а т ь класс JSObject в аплет,
3. Получить в аплете JavaScript-ссылку на текущее окно.
4. П р о ч и т а т ь требуемые JavaScript-свойства.
5. Установить требуемые JavaScript-свойства.
6. Вызвать требуемые JavaScript-методы.
7. Предоставить аплету разрешение на доступ к Web-странице.
Н и ж е эти действия описаны детально.

П о л у ч е н и е и и н с т а л л я ц и я класса J S O b j e c t
Класс J S O b j e c t входит в поставку Netscape 4 в составе JAll-файла с именем
j a v a 4 0 . Расположение этого файла может быть различным для разных операци­
о н н ы х систем. В Windows 98 он находится в каталоге
NetscapeInstallPath\Program\Java\Classes\
Здесь N e t s c a p e I n s t a l l P a t h означает главный каталог, в котором инсталлиро­
ван Netscape. Если файл в этом каталоге отсутствует, его можно найти, вызвав
пункт Find (Найти) главного меню системы.
В системе UNIX этот файл обычно находится в том каталоге, в котором инсталли­
рован броузер Netscape. П р и необходимости его можно найти с помощью следую­
щих команд:
Unix> cd / u s r / l o c a l
Unix> f i n d . -name j a v a 4 0 - p r i n t
Получив требуемый файл, его надо указать в переменной окружения CLASS PATH;
компилятор Java способен находить требуемые классы в JAR-файле. Перед запуском
a p p l e t v i e w e r переменную окружения CLASSPATH надо отключить, в противном
случае программа будет выполняться некорректно. Поскольку и Netscape, и Internet
Explorer предоставляют классам, указанным в CLASSPATH, специальные привилегии,
желательно отключить CLASSPATH перед запуском любого броузера. П р и желании
вы можете распаковать JAR-файл, вызвав в командной строке команду j a r x f
j a v a 4 0 . j a r , извлечь файл J S O b j e c t . c l a s s и установить его отдельно. Вам обяза­
тельно придется сделать это, если в системе, в которой вы работаете, не установлен
броузер Netscape. Возможно, вам потребуется включить класс J S O b j e c t в состав ва­
шего аплета, так как на клиентской машине может быть установлен другой броузер.
1158 Глава 24. JavaScript.

Импортирование класса JSObject в аплет


В начало аплета следует включить следующую строку кода:
import n e t s c a p e . j a v a s c r i p t . J S O b j e c t ;

Получение JavaScript-ссылки на текущее окно


Чтобы получить ссылку на окно, содержащее аплет, надо вызвать метод
getWindow класса JSObject:
JSObject window =
JSObject.getWindow(this); // this=aплeт
Список методов класса JSOb j e c t приведен в конце данного раздела.

Чтение требуемых JavaScript-свойств


Для того чтобы прочитать свойства главного окна JavaScript, надо использовать
метод getMember. Затем с помощью метода getMember читаются свойства других
JavaScript-объектов. Например:
JSObject document =
(JSObject)window.getMember("document");
S t r i n g cookies =
(String)document.cookie;
JSObject someForm =
(JSObject)document.getMember("someFormName");
JSObject someElement =
(JSObject)someForm.getMember("someElementName");
Для доступа к элементам массива можно использовать метод g e t S l o t .

Установка требуемых JavaScript-свойств


На данном этапе применяется метод setMember. Например:
document.setMember("bgColor", " r e d " ) ;
someElement.setMember("value", " t e x t f i e l d v a l u e " ) ;
Заметьте, что второй параметр метода setMember должен иметь тип Object, поэтому
простые типы необходимо преобразовывать в соответствующие классы. Так, напри­
мер, переменная типа i n t с именем i n t V a l u e преобразуется в объект I n t e g e r по­
средством выражения new I n t e g e r ( i n t V a l u e ) ; после этого созданный объект мо­
жет быть использован в качестве параметра. Кроме того, вы можете создать строку,
соответствующуюJavaScript-выражению, и передать ее методу e v a l (см. ниже).

Вызов требуемых JavaScript-методов


Для вызова JavaScript-метода надо либо использовать метод c a l l , либо создать
JavaScript-выражение, содержащее требуемый вызов, и указать его в качестве па­
раметра метода e v a l . Методу c a l l передается имя функции и массив параметров;
методу e v a l передается одна строка. Например:
S t r i n g [ ] message = { "An a l e r t message" };
w i n d o w . c a l l ( " a l e r t " , message);
window.eval("alert('An a l e r t message')");
24.1 о. Доступ к средствам JavaScript из Java 1159

Р а з р е ш е н и е д о с т у п а к Web-странице
Ч т о б ы аплет мог обращаться к Web-странице, в которой он расположен, автор до­
кумента должен явным образом р а з р е ш и т ь аплету выполнять подобные действия.
Это предотвращает случайную модификацию документа. Для предоставления прав
доступа используется атрибут MAY SCRIPT элемента APPLET. Н а п р и м е р :
<APPLET CODE=... WIDTH=... HEIGHT=... MAYSCRIPT>

</APPLET>

Определение цвета фона Web-страницы


Одна из проблем, возникающих при написании аплетов широкого назначения, со­
стоит в том, что аплет не может определить цвет ф о н а Web-страницы, в к о т о р о й он
содержится, и поэтому не может соответствующим образом установить цвет ф о н а вы­
деленной ему области. Часто, чтобы решить эту проблему, цвет ф о н а передают аплету
в качестве параметра; для этого используется элемент PARAM. Подобное р е ш е н и е
нельзя назвать удачным, поскольку в этом случае цвет фона указывается дважды (один
раз в дескрипторе <BODY> и второй раз в дескрипторе <PARAM>). П р и модификации
Web-страницы легко допустить ошибку. Благодаря LiveConnect аплет может опреде­
лять цвет ф о н а посредством объекта Document и устанавливать его автоматически.
Код, реализующий данный подход, приведен в листингах 24.27 и 24.28.

Листинг 2 4 . 2 7 . MatchColor. j a v a

import J a v a . a p p l e t . A p p l e t ;
import j a v a . a w t . * ;
import netscape.javascript.JSObject;

public class MatchColor extends Applet {


public void initO {
JSObject window = JSObject.getWindow(this); // this=aплeт
JSOb ject document = (JSOb ject) window. getMember ("dociMnent") ;
// Например, "#ffOOOO" для красного цвета
String pageColor = (String)document.getMember("bgColor");
// Например, parseint("ff0000", 16) --> 16711680
int bgColor =
Integer.parseint(pageColor.substring(1, 7 ) , 16);
setBackground(new Color(bgColor));
}
}

Листинг 24.28. MatchColor. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>MatchColor</TITLE>
</HEAD>
1160 Глава 24. JavaScript...

<BODY BGCOLOR="RED">
<Hl>MatchColor</Hl>
<APPLET CODE="MatchColor.class" WIDTH=300 HEIGHT=300 MAYSCRIPT>
</APPLET>
</BODY>
</HTML>

Аплет, проверяющий данные формы


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

Листинг 2 4 . 2 9 . E v e r e s t . html

<1D0CTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Design Your T r e k ! < / T I T L E >
</HEAD>
<BODY>

<APPLET C O D E = " E v e r e s t . c l a s s " WIDTH=400 HEIGHT=600


MAYSCRIPT ALIGN="LEFT">
</APPLET>

<H1 ALIGN="CENTER">Design Your T r e k ! < / H l >


To s e e a l i s t i n g of t h e t r e k s t h a t i n t e r e s t y o u , e n t e r t h e
d e s i r e d a l t i t u d e (up t o 2 9 , 0 0 0 f e e t ) and t h e maximum c o s t you
t h i n k y o u r b u d g e t c a n a f f o r d . Then c h o o s e "Show T r e k s " b e l o w .
W e ' l l show a l i s t of a l l p l a n n e d High P e a k s T r a v e l e x p e d i t i o n s
t h a t a r e u n d e r t h a t p r i c e and r e a c h t h e d e s i r e d a l t i t u d e o r
higher.
<P>
You can enter values directly in the textfields. Alternatively,
select a cost with the slider. Also, clicking the mouse on the
mountain peak will set the altitude. i
<CENTER>
<FORM ACTION="servlet/trekOptions" NAME="highPeaksForm">
<B>Desired Altitude:</B>
<INPUT TYPE="TEXT" NAME="altitudeField">
<BR>
<B>Maximum Cost:</B>
<INPUT TYPE="TEXT" NAME="costField">
<BR>
2 4 . 1 0 . Доступ к средствам JavaScript из Java 1161

<INPUT TYPE="SUBMIT" VALUE="Show Treks">


</FORM>
</CENTER>
</BODY>
</HTML>

Листинг 24.30.Everest.java

import Java.applet.Applet;
import java.awt.*;
import Java.awt.event.^;
import netscape.j avascript.JSObject;

/** Аплет, отображающий картинку и линейный регулятор.


* При изменении положения ползунка линейного регулятора
* изменяется содержимое одного из полей редактирования
* в HTML-файле, включающем аплет. При перемещении курсора
* мыши по изображению изменяется содержимое другого поля
* в составе HTML-документа. Нижняя часть изображения
* соответствует высоте О, а верхняя часть - высоте
* 2 9000 футов. В HTML-файле содержится форма с именем
* highPeaksForm, в которой находятся два поля редактирования
* с именами costField и altitudeField. В состав дескриптора
* <APPLET ...> должен быть указан атрибут MAYSCRIPT.
V
public class Everest extends Applet {
private Image mountains-
private JSObject window, document, highPeaksForm,
costField, altitudeField;
private int width, heights-
public void init() {
setBackground(Color.lightGray);
mountain = getlmage(getCodeBase(), "images/peakS.gif");
width = getSize (). widths-
height = getSize().height;
// Начать загрузку изображения,
preparelmage(mountain, width, height, this);
setLayout(new BorderLayout());
Font sliderFont = new Font("Helvetica", Font.BOLD, 18);
LabeledCostSlider costSlider =
new LabeledCostSlider("Specify a maximum cost:",
SliderFont, 2000, 20000, 5000,
this);
add(costSlider, BorderLayout.SOUTH);
addMouseMotionListener(new MouseMotionAdapter() {
// При перемещении курсора мыши значение изменяется
// от 29000 (верхняя часть) до О (нижняя часть). Это
// значение передается внешнему полю редактирования
// средствами JavaScript.
public void mouseMoved(MouseEvent event) {
1162 Глава 24. JavaScript...

System.out.println("Mouse Move at : " + event.getY());


setAltitudeField((height - event.getY()) * 29000 / height);
}
});

// Получение ссылок на поля редактирования HTML


// средствами JavaScript.
window = JSObject.getWindow(this) ; // ЪЫ8=аплвт
dociiment = (JSObject) window. getMember ("docioment") ;
highPeaksForm =
(JSObject) docimient. getMember ("highPeaksForm") ;
costField =
(JSObject)highPeaksForm.getMember("costField");
altitudeField =
(JSObject)highPeaksForm.getMember("altitudeField");
setCostField(5000);
setAltitudeField(15000);
}

public void paint(Graphics g) {


g.drawlmage(mountain, 0, 0, width, height, this);
}
/** Изменение поля редактирования средствами JavaScript. */

public void setCostField(int val) {


costField.setMember("value", String.valueOf(val));
}

/** Изменение поля редактирования средствами JavaScript. */

private void setAltitudeField(int val) {


altitudeField.setMember("value", String.valueOf(val));
}

В аплете E v e r e s t используется линейный регулятор, помеченный надписью. Он


реализован с помощью объекта L a b e l e d C o s t S l i d e r и показан в нижнем левом углу
на рис. 24.33. Исходный код класса L a b e l e d C o s t S l i d e r показан в листинге 24.31.
Класс C o s t S l i d e r (листинг 24.32) является подклассом класса S l i d e r (листинг
24.33) и при движении ползунка обновляет значение в поле, соответствующем стои­
мости. Класс S l i d e r объединяет горизонтальную полосу прокрутки S c r o l l b a r и
расположенный справа компонент Text F i e Id.
2 4 . 1 0 . Доступ к средствам JavaScript из Java 1163

tniglyj
тзшшшшшшш
^•&1 £<£t ^«m Qp Qemttjeioeiat ]ЬИ>

Design Your
Trek!
"I i. jee a bstaig of the treks ttat loterest
you, tract tbe dusked ahitude (t^ to
29,000 feet) аЫ liie т а я т ш п cost you
йаск your budget can afford. Tlxea
dwose "Show Treks" belofw. Well
show 8 list of аД pk&ned УИф Р е а Ь
Travel expedmons that arc under i b ^
price and reach the desired altitude or
hi^cr'

You car. enter vabcs directly b the


testfields. AJternativdy, select a cost
with the sSder, Also, cbcking the mouse
on the mmwitajn peak '-^^i^ ^^'^ tv.».
altitude.

Desired AJtthiid^

Mkdmnin Cost:

Specify а maximum cost:


j-ji^us'

Рис. 24.33. При перемещении курсора мыши по изображению и при


изменении положения линейного регулятора изменяются значения в
полях HTML-формы

Листинг 24.31. LabeledCostSlider. java

import j ava.awt.*;

/** Объект CostSlider с надписью, расположенной по центру. */

public class LabeledCostSlider extends Panel {


public LabeledCostSlider(String labelString,
Font labelFont,
int minValue, int maxValue,
int initialValue,
Everest app) {
setLayout(new BorderLayout());
Label label = new Label(labelString, Label.CENTER);
if (labelFont != null) {
label.setFont(labelFont) ;
}
add(label, BorderLayout.NORTH);
CostSlider slider = new CostSlider(minValue,
1164 Глава 24. JavaScript...

maxValue^
initialValue,
app) ;
add(slider, BorderLayout.CENTER);
}
}

Листинг 24.32.CostSlider.java

/** Объект Slider, конструктор которого получает в качестве


* параметра аплет Everest и обращается к методу setCostField
* аплета при изменении значения линейного регулятора.
Ч
public class CostSlider extends Slider {
private Everest app;
public CostSlider(int minValue, int maxValue,
int initialValue, Everest app) {
super(minValue, maxValue, initialValue);
this.app = app;
}
public void doAction(int value) {
app.setCostField(value);
}

Листинг 24.33.Slider.java

import j ava.awt.*;
import Java.awt.event.*;
/** Класс, содержащий горизонтальную полосу прокрутки
* (компонент Scrollbar) и поле редактирования (компонент
* TextField) справа от полосы прокрутки. В поле
* редактирования отображается текущее значение линейного
* регулятора, кроме того, если задано setEditable(true),
* оно может быть непосредственно использовано для изменения
* значения.
Ч
public class Slider extends Panel implements ActionListener,
AdjustmentListener {
private Scrollbar scrollbar;
private TextField textfield;
private ScrollbarPanel scrollbarPanel;
private int preferredWidth = 250;
/** Построение линейного регулятора с заданными минимальным,
* максимальным и начальным значениями. Размер ползунка
* устанавливается равным 1/10 от размера полосы.
V
24.10. Доступ к средствам JavaScript из Java 1165

public Slider(int minValue, int maxValue, int initialValue)


this(minValue, maxValue, initialValue,
(maxValue - minValue)/10);
}
/** Построение линейного регулятора с заданными минимальным,
* максимальным и начальным значениями, а также размером
* ползунка. Размер ползунка устанавливается не в пикселях,
* а в единицах шкалы регулятора. Так, если минимальное
* значение равно 20, а максимальное - 320, то размер
* ползунка 30 означает, что он будет занимать 10%
* видимой области.
*/
public Slider(int minValue, int maxValue, int initialValue,
int bubbleSize) {
setLayout(new BorderLayout());
maxValue = maxValue + bubbleSize;
scrollbar = new Scrollbar(Scrollbar.HORIZONTAL,
initialValue, bubbleSize,
minValue, maxValue);
scrollbar.addAdjustmentListener(this) ;
scrollbarPanel = new ScrollbarPanel(6);
scrollbarPanel.add(scrollbar, BorderLayout.CENTER);
add(scrollbarPanel, BorderLayout.CENTER);
textfield = new TextField(numDigits(maxValue) + 1 ) ;
textfield.addActionListener(this);
setFontSize(12);
textfield.setEditable(false);
setTextFieldValue();
add(textfield, BorderLayout.EAST);
}
/** Данный метод переопределяется для выполнения действий
* при изменении значения регулятора.

public void doAction(int value) {


}
/** При изменении содержимого поля редактирования
* изменяется состояние линейного регулятора.

public void actionPerformed(ActionEvent event) {


String value = textfield.getText();
int oldValue = getValueO;
try {
setValue(Integer.parseInt(value.trim()));
} catch(NumberFormatException nfe) {
setValue(oldValue);
}
}
/** При изменении состояния линейного регулятора
* изменяется содержимое поля редактирования.

public void adjustmentValueChanged(AdjustmentEvent event) {


1166 Глава 24. JavaScript.

setTextFieldValue();
doAction(scrollbar.getValue());
}
/** Возвращается объект Scrollbar, входящий в состав Slider. */

public Scrollbar getScrollbar() {


return(scrollbar);
}
/** Возвращается объект TextField, входящий в состав Slider */
public TextField getTextField() {
return(textfield);
}
/•• Установка минимальной ширины линейного регулятора,
* поскольку работать с регулятором небольшого размера неудобно.

public Dimension getPreferredSize() {


Dimension d = super.getPreferredSize();
d.height = textfield.getPreferredSize().height;
d.width = Math.max(d.width, preferredWidth);
return(d);
}
public Dimension getMinimumSize() {
return(getPreferredSize() ) ;
}
/** Для того чтобы линейный регулятор был хорошо виден,
* устанавливается минимальная ширина. Данный метод
* возвращает текущее значение (по умолчанию равно 150).

public int getPreferredWidthО {


return(preferredWidth) ;
}
/** Для того чтобы линейный регулятор был хорошо виден,
* устанавливается минимальная ширина. Данный метод
* устанавливает текущее значение (по умолчанию 150).

public void setPreferredWidth(int preferredWidth) {


this.preferredWidth = preferredWidth;
}
/** Возвращает текущее значение регулятора */
public int getValueО {
return(scrollbar.getValue());
}
/•• Устанавливает значение линейного регулятора. Если оно
* меньше минимального или превышает максимальное,
* устанавливается соответственно минимальное или
* максимальное значение.
*/
24.1 о. Доступ к средствам JavaScript из Java 1167

public void setValue(int value) {


scrollbar.setValue(value);
setTextFieldValue();
}
/** Возвращает границы горизонтальной полосы прокрутки^
* используемой в качестве линейного регулятора.

public int getMarginsO {


return(scrollbarPanel.getMargins());
}
/•• Установка границ горизонтальной полосы прокрутки,
* используемой в качестве линейного регулятора.

public void setMargins(int margins) {


scrollbarPanel.setMargins(margins);
}
/** Возвращает строку в поле редактирования. В большинстве
* случаев действует как getValue, но значение может
* быть дополнено слева пробелами.
V
public String getText() {
return(textfield.getText() ) ;
}
/•• Устанавливает значение поля редактирования. При
"^ использовании данного метода надо соблюдать осторожность,
* поскольку он не проверяет, является ли значение числом,
* и не выравнивает его по правой границе.
V
public void setText(String text) {
textfield.setText(text) ;
}
/** Возвращает объект Font, используемый в поле
* редактирования. По умолчанию используется полужирный
* шрифт Courier размером 12 пунктов.

public Font getFontO {


return(textfield.getFont());
}
/** Изменяет шрифт, используемый в поле редактирования. */

public void setFont(Font textFieldFont) {


textfield.setFont(textFieldFont);
}
/** Возвращает текущий размер шрифта. */
public int getFontSize() {
return(getFont().getSize());
}
1168 Глава 24. JavaScript.

/** Вместо того чтобы устанавливать шрифт, можно задать


* его размер с помош[ью данного метода.
V
public void setFontSize(int size) {
setFont(new Font("Monospaced", Font.BOLD, size));
}
/** Позволяет определить, разрешено ли изменять текст
* в поле редактирования. Если редактирование разрешено,
* вы можете ввести значение, в результате чего положение
* линейного регулятора изменится. Если указанное значение
* выходит за пределы допустимого диапазона, выбирается
* соответственно минимальная или максимальная величина.
* Нечисловые значения игнорируются.
V
public boolean isEditableO {
return(textfield.isEditable());
}
/** Определяет, можно ли непосредственно вводить значение
* в поле редактирования.

public void setEditable(boolean editable) {


textfield.setEditable(editable);
}
// Устанавливает в поле редактирования числовое значение,
// выровненное вправо.
private void setTextFieldValue () {
int value = scrollbar.getValue();
int digits = numDigits(scrollbar.getMaximum());
String valueString = padString(value, digits);
textfield.setText(valueString);
}
//Конкатенация в цикле неэффективна, но данный подход
// используется только для добавления небольшого количества
// пробелов.
private String padString(int value, int digits) {
String result = String.valueOf(value);
for(int i=result.length 0 ; i<digits; i++) {
result = " " + result;
}
return(result + " " ) ;
}
// Определяет количество цифр в десятичном числе.
private static final double LNIO = Math.log(10.0);
private static int numDigits(int num) {
return(1 + (int)Math.floor(Math.log((double)num)/LNIO));
}
2 4 . 1 0 . Доступ к средствам JavaScript из Java 1169

Листинг 24.34. Scrollbar Panel. Java

import j a v a . a w t . * ;
/** Объект Panel с изменяемыми верхней и нижней границами.
V
public class ScrollbarPanel extends Panel {
private Insets insets;

public ScrollbarPanel(int margins) {


setLayout(new BorderLayout());
setMargins(margins);
}
public Insets insets 0 {
return(insets) ;
}
public int getMarginsO {
return(insets.top);
}
public void setMargins(int margins) {
this.insets = new Insets(margins, 0, margins, 0 ) /
}
}

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


в классе JSObject доступны перечисленные ниже методы. Класс JSObject объ­
явлен как f i n a l , т. е. подкласс данного класса не может быть создан.

public Object call(String methodName, Object[] args)


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

public Object eval(String javaScriptCode)


Этот метод дает возможность выполнять произвольные JavaScript-выражения.

public Object getMember(String propertyName)


Метод getMember возвращает значение свойства. Результат необходимо преобра­
зовать в соответствующий тип.

public Object getSlot(int array Index)


Этот метод возвращает элемент массива. Результат необходимо преобразовать в
соответствующий тип.
1170 Глава 24. JavaScript...

p u b l i c static J S O b j e c t g e t W i n d o w ( A p p l e t applet)
Данный статический метод позволяет получить JavaScript-объект Window, соот­
ветствующий окну, содержащему аплет.

public void r e m o v e M e m b e r ( S t r i n g p r o p e r t y N a m e )
Метод removeMember удаляет указанное свойство.

public void s e t M e m b e r ( S t r i n g p r o p e r t y N a m e , Object value)


Этот метод устанавливает значение указанного свойства.

public v o i d setSlot(int a r r a y l n d e x , Object value)


Данный метод задает значение указанного элемента массива.

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

• Создание HTML-кода в процессе загрузки Web-страницы.


• Включение динамических элементов в состав документа.
• Проверка данных HTML-формы.
• Обработка cookie.
• Работа с фреймами.
• Обеспечение взаимодействия Java и JavaScript.
В данной главе не были рассмотрены Window, Document, N a v i g a t o r и п р о ч и е
стандартные объекты JavaScript 1.2. В следующей главе будут подробно описаны кон­
структоры, свойства и методы JavaScript-объектов.
КРАТКОЕ
РУКОВОДСТВО
ПО JAVASCRIPT

В ЭТОЙ главе...

• Объекты, связанные с броузером, и их окружение: Naviga­


t o r , P l u g i n , Screen И другие высокоуровневые объекты.
• Объекты, соответствующие HTML-элементам: window.
Document, L a y e r , Image И Др.
• Объекты, соответствующие HTML-формам и интерфейс­
ным элементам (Form, Text, B u t t o n , S e l e c t ) , И Другие
объекты, используемые для работы с программами на сто­
роне сервера.
• Внутренние структуры данных: s t r i n g . A r r a y , F u n c t i o n ,
Math, Date И другие вспомогательные объекты.
• Регулярные выражения: объект RegExp.
Lry\zj.£3z:j

предыдущей главе вы познакомились с JavaScript и примерами использования

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


чики событий, соответствующие JavaScript 1.2. JavaScript 1.2 поддерживается во
всех версиях Netscape, в Internet Explorer 4.0 и более новых версиях. И н ф о р м а ц и ю об
особенностях последних версий JavaScript вы найдете в документе h t t p : / / d e v e l o p e r .
netscape.com/docs/manuals/javascript.html.

2 5 . 1 . Объект Array
В первой версии JavaScript массивы реализовывались как объекты с множествен­
ными свойствами. Позже в JavaScript 1.1 для поддержки массивов был введен отдель­
ный объект A r r a y .

Конструкторы
n e w АггауО
Д а н н ы й конструктор создает новый массив нулевой длины. П р и включении ново­
го элемента с указанием индекса длина массива изменяется. Н а п р и м е р :
v a r а = new A r r a y ( ) ; / / а.length = О
а[12] = "foo"; / / а . l e n g t h = 13

n e w Array(length)
Рассматриваемый конструктор создает массив указанной длины. Элементы масси­
ва идентифицируются с помощью индекса, лежащего в пределах от О до l e n g t h - 1 .
Сразу после создания массива все элементы инициализируются значениями n u l l .
1174 Глава 25. Краткое руководство по JavaScript

Заметьте, что данный конструктор не поддерживается в JavaScript 1.2, реализо­


ванном в Netscape. Вместо того чтобы создавать массив с указанным числом эле­
ментов, он создает массив, состоящий из одного элемента, который инициализи­
рован значением, переданным конструктору в качестве параметра. В Internet
Explorer эта проблема не возникает. Компания Netscape устранила данный недос­
таток в версии JavaScript 1.3.

n e w Array(entryO, entry 1 , . . . , entryN)


Этот конструктор создает массив длиной N, содержащий указанные элементы.

[entryO, entry 1 , . . . , e n t r y N ]
Данный конструктор представлен в "литеральном" виде. Н а п р и м е р , следующие
два конструктора дают одинаковые результаты:
v a r a l = new A r r a y C ' f o o " , " b a r " , " b a z " ) ;
v a r a2 = [ " f o o " , " b a r " , " b a z " ] ;

Свойства
length
Данное свойство определяет число элементов в массиве. Ч и с л о элементов на еди­
ницу превышает значение последнего индекса. Свойство l e n g t h допускает как
чтение, так и запись. Если вы присвоите данному свойству новое значение меньше
текущего, последние элементы массива будут утеряны. Если присваиваемое значе­
ние превышает текущее, в состав массива будут включены новые элементы, кото­
рые получат специальное значение u n d e f i n e d (при сравнении этого значения с
n u l l с помощью оператора == возвращается t r u e ) .

Методы
concat(secondArray)
Данный метод возвращает новый массив, созданный путем конкатенации текуще­
го массива и массива, указанного в качестве параметра ( s e c o n d A r r a y ) .

join()
j o i n ( d e l i m i t e r String)
Первый из указанных методов возвращает строку, которая получена путем преобразо­
вания всех элементов массива в строковые значения с последующей их конкатенацией.
Второй метод отличается от первого тем, что в результирующей строке между элемен­
тами массива помещается строка, определяемая параметром d e l i m i t e r S t r i n g (перед
первым элементом и после последнего эта строка не включается).

reverse()
Данный метод перестраивает элементы текущего массива в обратном порядке.
Н о в ы й массив не создается.
2 5 . 1 . Объект Array 1175

slice(startlndex)
slice(startlndex, endlndex)
Метод s l i c e возвращает новый массив, состоящий из элементов с индексами от
s t a r t l n d e x до endlndex, извлеченных из текущего массива. Элемент s t a r t l n d e x
входит в состав нового массива, а элемент endlndex не входит в новый массив.
sort()
sort(comparisonFunction)
Первый из указанных методов (т. е. метод s o r t без параметров) размещает эле­
менты массива в алфавитном порядке, при этом новый массив не создается. Для
второго метода порядок размещения элементов определяется функцией сравне­
ния ( c o m p a r i s o n F u n c t i o n ) . При вызове этой функции ей в качестве параметров
передаются два элемента массива. В результате выполнения функции возвращает­
ся отрицательное значение, если первый параметр "меньше" второго, нулевое
значение, если параметры "равны", и положительное значение, если первый па­
раметр "больше" второго. Ниже приведена функция сравнения, которая получает
при вызове два объекта Саг и сравнивает свойства maxSpeed этих объектов.
function slower(carl, саг2) {
return(carl.maxSpeed - car2.maxSpeed);
}
В листинге 25.1 данный подход применяется при создании массива объектов Саг и
последующей сортировке массива в соответствии с максимальной скоростью
(свойство maxSpeed). Результаты выполнения сценария показаны на рис. 25.1.

Листинг25.1. Sort.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">


<HTML>
<HEAD>
<TITLE>Sorting</TITLE>
<SCRIPT TYPE="text/javascript">
<! —
function makeObjectTable(name, object) {
document.writeln("<H2>" + name + "</H2>") ;
document.writeln("<TABLE BORDER=l>\n" +
" <TR><TH>Field<TH>Value") ;
for(field in object) {
document.writeln(" <TR><TD>" + field + "<TD>" +
object[field]);
}
document.writeln("</TABLE>");
}
// — >
</SCRIPT>
</HEAD>
<BODY>
<Hl>Sorting</Hl>
<SCRIPT TYPE="text/javascript">
<! —
1176 Глава 25. Краткое руководство по JavaScript

function carStringO {
return("Car{" + this.maxSpeed + " } " ) ;
}
function Car(maxSpeed) {
this.maxSpeed = maxSpeed;
this.toString = carString;
}
function slower(carl, car2) {
return(carl.maxSpeed - car2.maxSpeed);
}
var cars = new Array(new Car(10), new Car(20),
new Car(30), new Car(25),
new Car(15), new Car(5));
// ~ >
</SCRIPT>
<TABLE>
<TR><TD>
<SCRIPT TYPE="text/javascript">
<! —
makeObjectTable("Original Car Array", cars);
// — >
</SCRIPT>
<TD><PRE> </PRE>
<TD><SCRIPT TYPE="text/javascript">
<! —
cars.sort(slower) ;
makeObjectTable("Sorted Array (slow to fast)", cars)
// — >
</SCRIPT>
</TABLE>
</BODY>
</HTML>

Ешгаш
file £А' y«w Favorite» lixjl» Hefc ""

"H
Sorting
Original Car Array Sorted Arra

Field Value ТкИ V»l«e


0 Car{lO} 10 Cttr{5}
1 Cai{20} il C<u-{10}
2 .Car{ 30} : b Саг{15}
3 :Car{25} ;3 Car {20}
4 :Сйх{15} |4 С4г{25}
5 Car{5} ,5 Car{30)

J Dane :х/<:ЬМ?<-1'Ш1" ''^iAifCcnpdtmW^%

Рис. 25.1. JavaScript позволяет сортировать мас­


сивы с применением функции сравнения, опреде­
ляемой пользователем
25.2. Объект Button 1177

Обработчики событий
Обработчики событий отсутствуют.

25.2. Объект Button


Объект B u t t o n соответствует кнопке, т.е. элементу ф о р м ы , созданному с помо­
щью дескриптора <INPUT TYPE="BUTTON" . . . >. Большинством характеристик
элемента B u t t o n обладают также элементы, созданные посредством дескрипторов
<INPUT TYPE="SUBMIT" ...>H<INPUT TYPE="RESET" . . . > , однако ДЛЯ КЕЮПОК
SUBMIT и RESET определены специальные типы S u b m i t и R e s e t . Для доступа к эле­
менту B u t t o n используется массив e l e m e n t s соответствующей ф о р м ы либо, если и
форме, и элементу присвоены имена, выражение d o c u m e n t . f ormName . b u t t o n N a m e ,
где f ormName — имя ф о р м ы , a b u t t o n N a m e — имя элемента.

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

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

type
Для обычных объектов B u t t o n данное свойство имеет значение b u t t o n . Для кнопок
SUBMIT и RESET данное свойство содержит соответственно значение s u b m i t и r e ­
s e t . Объект E l e m e n t содержит свойство t y p e , которое может использоваться для
определения типа элемента. Свойство t y p e предназначено только для чтения.

value
Данное свойство предназначено как для чтения, так и для записи и определяет
надпись на кнопке. В случае кнопки SUBMIT значение этого свойства передается
на сервер вместе с именем элемента.

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

clickO
Данный метод выполняет те же действия, которые выполняются после щелчка
мышью на кнопке, но не генерирует событие o n C l i c k . Для кнопок SUBMIT и
RESET вместо c l i c k можно использовать методы s u b m i t и r e s e t формы.
1178 Глава 25. Краткое руководство по JavaScript

focus()
в результате выполнения метода f o c u s кнопка получает фокус ввода.

Обработчики событий
onblur()
Данный метод получает управление при потере кнопкой фокуса ввода. О б ы ч н о он
определяется посредством атрибута o n B l u r , как показано ниже.
<INPUT TYPE="BUTTON" . . .
onBlur="doSomeAction()">

onclickO
Метод o n c l i c k вызывается тогда, когда пользователь щелкает мышью на кнопке.
П р и вызове метода c l i c k метод o n c l i c k управление не получает. Обычно дан­
ный метод определяется с помощью атрибута o n C l i c k , как показано ниже.
<INPUT TYPE="BUTTON" . . .
onClick="doSomeAction()">
Если данный метод возвращает значение f a l s e , дополнительные действия, свя­
занные с активизацией кнопки (например, передача данных на сервер или сброс
ф о р м ы ) , подавляются. Например:
<INPUT TYPE="RESET" . . .
onClick="return(maybeReset())">
Так же ведут себя обработчики событий o n S u b m i t и o n R e s e t ф о р м ы , содержащей
кнопку.

ondblclickO
Метод o n d b l c l i c k вызывается после двойного щелчка мышью. После первого
щелчка вызывается метод o n c l i c k . Данный метод определяется с помощью атри­
бута o n D b l C l i c k ; он не поддерживается Macintosh и Netscape 6.

onfocusO
Метод on f o c u s вызывается тогда, когда кнопка получает фокус ввода. Для опре­
деления данного обработчика используется атрибут o n F o c u s .

25.3. Объект Checkbox


Объект C h e c k b o x соответствует флажку опции, т.е. элементу формы, создаваемому с
помощью дескриптора <INPUT TYPE="CHECKBOX" . . . >. Для доступа к элемент)^
Checkbox используется массив e l e m e n t s формы либо, если и форме, и элементу при­
своены имена, — выражение d o c u m e n t . f ormName . checkboxName, где f ormName — имя
формы, a checkboxName — имя элемента.
25.3. Объект Checkbox 1179

Свойства
checked
Данное свойство, предназначенное как для чтения, так и для записи, содержит ло­
гическое значение, которое определяет, установлен ли в данный момент флажок
опции.

defaultChecked
Свойство d e f a u l t C h e c k e d содержит логическое значение, определяющее, дол­
жен ли флажок опции быть изначально установлен. Значение этого свойства зада­
ется посредством атрибута CHECKED и предназначено только для чтения.

form
Данное свойство, предназначенное только для чтения, содержит объект Form, в
состав которого входит флажок опции.

Данное свойство, предназначенное только для чтения, содержит имя элемента,


установленное с помощью атрибута NAME.

type
Это свойство содержит последовательность символов " c h e c k b o x " . Поскольку
свойство t y p e имеют все объекты E l e m e n t , оно может быть использовано для
идентификации типа объекта, содержащегося в массиве f o r m , e l e m e n t s . Свойст­
во t y p e предназначено только для чтения.

value
Данное свойство, предназначенное как для чтения, так и для записи, содержит
значение элемента, которое вместе с именем элемента передается программе на
стороне сервера в случае, если флажок опции установлен.

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

clickO
Данный метод выполняется те же действия, которые выполняются после щелчка
мышью на элементе, но не генерирует событие o n C l i c k .

focus()
в результате выполнения метода f o c u s элемент получает фокус ввода.
1180 Глава 25. Краткое руководство по JavaScript

Обработчики событий
onblur()
Данный метод получает управление при потере флажком опции фокуса ввода.
О б ы ч н о он определяется посредством атрибута o n B l u r , как показано ниже.
<INPUT TYPE="CHECKBOX" . . .
onBlur="doSomeAction()">

onclickO
Метод o n c l i c k вызывается тогда, когда пользователь щелкает мышью на элемен­
те. П р и вызове метода c l i c k метод o n c l i c k управление не получает. Обычно
данный обработчик определяется с помощью атрибута o n C l i c k .

onfocusO
Метод o n f OCUS вызывается тогда, когда флажок опции получает фокус ввода. Для
определения данного метода обычно используется атрибут on F o c u s .

25.4. Объект Date


Объект D a t e предоставляет и н ф о р м а ц и ю о дате и времени и позволяет обрабаты­
вать ее.

Конструкторы
n e w Date()
Данный конструктор создает объект D a t e , соответствующий текущему времени.

n e w Date(year, m o n t h , day)
Этот конструктор создает объект D a t e , соответствующий началу указанных суток.

n e w Date(year, m o n t h , day, h r s , m i n s , sees)


Данный конструктор создает объект D a t e , соответствующий указанному времени.

n e w Date("month day, year hrs:mins:secs")


Приведенный конструктор преобразует заданную строку в объект D a t e . В строке
следует указывать полное название месяца (month), а не его номер. Например:
var bOay = new Date("January 30, 1962 00:00:00");

new Date(millisecondsSinceEpoch)
Данный конструктор создает объект D a t e , соответствующий указанному числу
миллисекунд с начала эпохи, т.е. с полуночи по гринвичскому времени (GMT) пер­
вого января 1970 г.
25.4. Объект Date 1181

Свойства
Свойства отсутствуют.

Методы
Заметьте, что описанные ниже p a r s e D a t e и UTC на самом деле не являются мето­
дами объекта D a t e , а работают как статические методы "класса" D a t e (т.е. конструк­
тора). Для их вызова надо использовать соответственно выражения D a t e . p a r s e D a t e
и Date.UTC. Другие методы вызываются с помощью выражения s o m e D a t e O b j e c t .
m e t h o d ( a r g s ) , где s o m e D a t e O b j e c t — объект D a t e , m e t h o d — имя метода, a a r g s —
параметры.

getDateO
setDate(dayOfMonth)
Данные методы предназначены для получения и установки информации о дне ме­
сяца. День месяца определяется целым числом в интервале от 1 до 31.

getDayO
Метод g e t Day возвращает день недели. День недели определяется числовым зна­
чением; О соответствует воскресенью, а 6 — субботе.

getHoursO

setHours(hours)
Указанные методы позволяют получить и задать число часов дня. Число часов за­
дается как целочисленное значение в интервале от О до 23.

getMinutesO
setMinutes(minutes)
Метод g e t M i n u t e s возвращает число минут часа, информация о котором получе­
на с помощью метода g e t H o u r s . Метод s e t M i n u t e s позволяет задать число ми­
нут. И н ф о р м а ц и я о минутах представляется в виде целого числа в интервале от О
до 59.

getMonthO
setMonth(monthlndex)
Данные методы позволяют получить и задать значение месяца. Месяц указывается
как число в диапазоне от О (январь) до 11 (декабрь).

getSecondsO
setSeconds(seconds)
Эти методы позволяют получить и задать число секунд, прошедших с начала мин)а'ы,
возвращаемой методом g e t M i n u t e s . Число секунд лежит в интервале от О до 59.
1182 Глава 25. Краткое руководство по JavaScript

getTimeO
setTime(millisecondsSince£poch)
Метод g e t T i m e возвращает, а метод s e t T i m e устанавливает число миллисекунд,
прошедших с полуночи про гринвичскому времени (GMT) 1 января 1970 г.

getTimezoneOffsetO
Данный метод возвращает разницу в минутах между GMT и локальным временем.

getFuUYearO
setFullYear(year)
Первый метод, g e t F u l l Y e a r , возвращает год, содержащийся в объекте D a t e , как
целое число, содержащее четыре знака. Второй метод, s e t F u l l Y e a r , устанавли­
вает год в объекте D a t e и возвращает число миллисекунд, прошедших с полуночи
1 января 1970 г. до момента, соответствующего значению объекта D a t e . Заметьте,
что методы g e t F u l l Y e a r / s e t F u l l Y e a r были введены в JavaScript 1.0 и в на­
стоящее время не рекомендованы для использования, так как в одних броузерах
год задается числом, содержащим два знака, в других — числом из четырех знаков.

parse(dateString)
Данный метод на самом деле не является методом объекта D a t e . К нему следует
обращаться с помощью выражения D a t e . p a r s e . В качестве параметра данному
методу передается строка в одном из допустимых форматов, а в результате выпол­
нения метода возвращается число миллисекунд, прошедших с полуночи 1 января
1970 г. Метод p a r s e поддерживает стандартный формат даты IETF, используемый
в Internet (генерируемый методом t o G M T S t r i n g ) . Например:
// Время тихоокеанского побережья США.
var dateString = "Wed, 3 Sep 1997 08:30:00 -0700";
var dl = new Date(Date.parse(dateString));
// Восточное время США.
document.writeln(dl.toLocaleString());
Метод p a r s e также может обрабатывать строки в формате "Month Day, Y e a r "
("Месяц День, Год"), где месяц задается полным именем либо первыми тремя бук­
вами (в верхнем или нижнем регистре). В формате IETF разрешается задавать
смещение временного пояса либо временные пояса США (например, EDT).

toGMTStringO
Данный метод генерирует строку, которая представляет дату и время, соответст­
вующие GMT. Строка форматируется по соглашениям IETF (см. описание метода
parse).

toLocaleStringO
Метод t o L o c a l e S t r i n g генерирует строку, которая представляет значение объ­
екта D a t e для локального временного пояса. Форматирование выполняется в со­
ответствии с соглашениями для локального пояса.
25.5. Объект Document 1183

UTC(year, m o n t h , day)
UTC(year, m o n t h , day, hrs)
UTC(year, m o n t h , day, h r s , mins)
UTC(year, m o n t h , day, hrs, m i n s , sees)
Указанные методы на самом деле не являются методами объекта D a t e . Обращать­
ся к ним надо с помощью выражения Date.UTC. Считается, что значения пара­
метра соответствуют гринвичскому времени, или GMT (оно также называется
универсальным временем, или UTC). В результате выполнения методов возвраща­
ется число миллисекунд, прошедших с полуночи 1 января 1970 г.

Обработчики событий
Обработчики событий отсутствуют. Объект D a t e не соответствует ни одному из
HTML-элементов.

25.5. Объект Document


Каждые! объект Window содержит свойство docглnent, которое ссылается на доку­
мент, содержащийся в окне. Обращение к документу производится с помощью выра­
жения w i n d o w , d o c u m e n t , или, чаще, d o c u m e n t .

Свойства
alinkColor
Данное свойство содержит строку, определяющую цвет активизированной ссыл­
ки. Это свойство задается с помощью атрибута ALINK дескриптора <BODY> и мо­
жет быть м о д и ф и ц и р о в а н о только сценариями в разделе HEAD документа (разбор
которого выполняется раньше, чем разбор раздела BODY). Свойство a l i n k C o l o r
предназначено только для чтения.

anchors
Свойство a n c h o r s позволяет обращаться к массиву объектов A n c h o r , каждый из
которых соответствует вхождению в документ дескриптора <А NAME= . . . >.

applets
Данное свойство обеспечивает дост)^п к массиву объектов A p p l e t , каждый эле­
мент которого соответствует вхождению в документ дескриптора <APPLET . . . >.
Если в составе дескриптора <APPLET> прис)^ствует атрибут MAYSCRIPT, вы може­
те непосредственно вызывать методы аплета из JavaScnpt-сценария. Соответст­
вующий пример см. в разделе 24.9.

bgColor
Данное свойство содержит строку, определяющую цвет фона документа. Первона­
чально значение данного свойства устанавливается с помощью атрибута BGCOLOR
1184 Глава 25. Краткое руководство по JavaScript

дескриптора <BODY>, но впоследствии может быть изменено. Свойство b g C o l o r


доступно как для чтения, так и для записи. Например:
document.bgColor = "red";
document.bgColor = "#OOFFOO"; // зеленый

cookie
Данное свойство, доступное как для чтения, так и для записи, содержит значения
cookie, связанных с документом. Установка свойства c o o k i e изменяет значения
cookie, сохраненные броузером (подробно об этом см. в разделе 24.7).

domain
Свойство d o m a i n содержит строку, определяющую Internet-домен, из которого
был получен документ. Данное свойство доступно только для чтения. В JavaScript
отсутствуют стандартные средства, позволяющие определить домен или имя узла
клиента (программы, с помощью которой пользователь просматривает документ,
но вы можете сделать это посредством Java-аплета. О вопросах взаимодействия
JavaScript и Java см. в разделе 24.9.

embeds
Данное свойство обеспечивает дост)'п к массиву объектов J a v a O b j e c t , соответст­
вующих встроенным объектам в составе элементов EMBED документа. Если встро­
енные объекты являются Java-объектами, вы можете вызывать их общедоступные
методы, в противном случае вам не удастся использовать их. Массив e m b e d s также
называется p l u g i n s .

fgColor
Указанное свойство содержит строку, определяющую цвет переднего плана доку­
мента. Первоначально значение f g C o l o r устанавливается посредством атрибута
TEXT дескриптора <BODY> и может быть изменено лишь сценарием, выполняю­
щимся в разделе HEAD документа. Свойство f g C o l o r дост)^пно только для чтения.

forms
Массив, состоящий из объектов Form, каждый из которых соответствует вхожде­
нию в документ дескриптора <FORM . . . >. Дополнительную информацию об объ­
екте Form вы найдете в разделе 25.8.

images
Массив объектов Image, каждый из которых соответствует вхождению в документ де­
скриптора <IMG . . . >. Примеры использования данного свойства см. в разделе 24.5.

lastModified
Свойство l a s t M o d i f i e d содержит дату последнего изменения документа. Для
пользователей, часто обращающихся к одной Web-странице за новой информаци­
ей, удобно, если значение l a s t M o d i f i e d будет отображаться в начале документа.
Данное свойство дост)'пно только для чтения.
25.5. Объект Document 1185

linkColor
Значением данного свойства является строка. Эта строка определяет цвет гипер­
текстовых ссылок, указывающих на документы, которые еще не просматривались
клиентом. Первоначально значение l i n k C o l o r устанавливается с помощью атри­
бута LINK дескриптора <BODY> и может быть изменено только сценарием в разде­
ле HEAD документа. Данное свойство дост)'пно только для чтения.

links
Данное свойство предоставляет доступ к массиву объектов L i n k , каждый из кото­
рых соответствует вхождению в документ дескриптора <А HREF. . . >.

location
Свойство l o c a t i o n , доступное только для чтения, ссылается на тот же объект
L o c a t i o n , что и свойство window, l o c a t i o n , и предоставляет доступ к URL, кото­
рый может быть изменен вследствие перенаправления. Свойство d o c u m e n t . URL со­
держит реальный URL.

plugins
Данное имя свойства является синонимом свойства e m b e d s . Заметьте, что массив
p l u g i n s содержит не объекты P l u g i n , а объекты типа J a v a O b j e c t и описывает
элементы, встроенные в текущий документ, а не дополнительные модули броузера.
Для того чтобы получить массив, описывающий дополнительные модули броузера,
следует использовать свойство n a v i g a t o r . p l u g i n s .

referrer
Значением свойства r e f e r r e r является строка (возможно, пустая), которая со­
держит URL документа. В этом документе находится ссылка, в результате активи­
зации которой была получена текущая Web-страница. Данное свойство доступно
только для чтения.

title
Свойство t i t l e содержит строку, определенную посредством дескриптора <TITLE>.
Это свойство доступно только для чтения.

URL
Строка, содержащая URL текущего документа. Свойство доступно только для чтения.

vlinkColor
Значением данного свойства является строка. Эта строка определяет цвет гипер­
текстовых ссылок, указывающих на документы, которые уже посещались клиен­
том. Первоначально значение v l i n k C o l o r устанавливается с помощью атрибута
VLINK дескриптора <BODY> и может быть изменено только сценарием в разделе
HEAD документа. После этого свойство v l i n k C o l o r доступно лишь для чтения.
1186 Глава 25. Краткое руководство по JavaScript

Методы
close()
Данный метод закрывает выходной поток для документа. В результате выводятся
данные, которые еще не были отображены. Совместно с методом o p e n метод
c l o s e используется при создании новых документов.

getSelectionO
Метод g e t S e l e c t i o n возвращает текст, содержащийся в выделенной области.

ореп()
open(mimeType)
o p e n ( m i m e T y p e , "replace")
Данный метод создает в текущем окне новый документ. Чаще всего используется
первый из приведенных вариантов метода, после чего с помощью методов w r i t e
и w r i t e l n включается содержимое документа. П р и вызове метода o p e n можно
также задать МШЕ-тип.
Если указан параметр "replace", новый документ замещает существующий в списке
предыстории, в противном случае создается новый пункт списка.

write(argl, a r g 2 , . . . , argN)
w r i t e l n ( a r g l , a r g 2 , . . . , argN)
Эти методы передают указанные данные документу; второй из приведенных мето­
дов завершает данные символом новой строки.

Обработчики событий
в объекте Document обработчики событий отсутствуют. Атрибуты o n l o a d и
o n u n l o a d дескриптора <BODY> определяют обработчики объекта Window, а не объ­
екта Document.

25.6. Объект Element


Объекты E l e m e n t соответствуют элементам формы и содержатся в массиве
e l e m e n t s объекта Form. Объект Form доступен посредством массива d o c u m e n t . f o r m s
или, если документу присвоено имя, посредством выражения d o c u m e n t . f ormName, где
f ormName — имя формы. К элементам массива e l e m e n t s можно обращаться не только
как к объектам E l e m e n t , но и как к объектам конкретных типов: B u t t o n , Checkbox и
т.д. Кроме того, элементам в составе формы можно присваивать имена и использовать
их при обращении.

Свойства
Перечисленные ниже свойства принадлежат не всем типам объектов E l e m e n t .
Подробно конкретные т и п ы элементов будут рассмотрены далее в этой главе; там же
будут описаны их свойства.
25.6. Объект Element 1187

checked
Данное свойство используется объектами Checkbox и Radio.

defaultChecked
Данное свойство используется объектами Checkbox и Radio.

defaultValue
Данное свойство используется объектами FileUpload, Password, Text и Textarea.

form
Данное свойство используется всеми объектами Element и ссылается на HTML-
форму, в состав которой входит элемент.

length
Данное свойство используется только объектом S e l e c t .

name
Данное свойство используется всеми объектами Element и содержит значение
HTML-атрибута NAME.

options
Данное свойство используется только объектом Select.

selectedlndex
Данное свойство используется только объектом Select.

type
В JavaScript LI это свойство используется всеми объектами Element и может быть
использовано для определения типа элемента. Свойство t y p e может принимать
следующие значения: b u t t o n , checkbox, f i l e , hidden, password, r a d i o , r e s e t ,
select-one, select-multiple,submit,text и textarea.

value
Данное свойство используется всеми объектами Element и содержит значение,
связанное с именем элемента. Значение вместе с именем передается на сервер.

Методы
Как и свойства, перечисленные ниже методы используются не всеми типами объ­
ектов Element. Дополнительная информация о методах приводится при рассмотре­
нии конкретных типов элементов.
1188 Глава 25. Краткое руководство по JavaScript

blurO
Данный метод используется всеми типами объектов E l e m e n t за исключением
объекта H i d d e n .

clickO
Данный метод используется объектами Button, Checkbox, Radio, Reset и Submit.

focus()
Данный метод используется всеми типами объектов E l e m e n t за исключением
объекта H i d d e n .

select()
Данный метод используется теми типами объектов E l e m e n t , которые имеют тек­
стовые значения, а именно: F i l e U p l o a d , P a s s w o r d , T e x t и T e x t a r e a .

Обработчики событий
Здесь перечислены типы объектов E l e m e n t и связанные с ними события.

onblur()
Данный метод используется всеми типами объектов E l e m e n t за исключением
объекта H i d d e n .

onchange()
Данный метод используется объектами F i l e U p l o a d , P a s s w o r d , T e x t и T e x t a r e a .

onclickO
Данный метод используется объектами Button, Checkbox, Radio, Reset и
Submit.

ondblclick()
Данный метод используется объектами B u t t o n , R e s e t и S u b m i t .

onfocus()
Данный метод используется всеми типами объектов E l e m e n t за исключением
объекта H i d d e n .

25.7. Объект FileUpload


Объект F i l e U p l o a d соответствует элементу ф о р м ы , определяемому с помощью
дескриптора <INPUT TYPE="FILE" . . . >. Объекты этого типа доступны посредст­
вом массива e l e m e n t s , входящего в состав объекта Form.
25.7. Объект FileUpload 1189

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

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

type
Д а н н о е свойство всегда содержит значение f i l e . Свойство t y p e соответствует
всем объектам E l e m e n t , поэтому оно о б ы ч н о используется для идентификации
типа элемента.

value
Значением свойства v a l u e является строка, заданная посредством атрибута
VALUE. Это свойство предназначено только для чтения.

Методы
blurO
В результате выполнения этого метода элемент теряет фокус ввода.

focus()
в результате выполнения этого метода элемент получает фокус ввода.

select()
Д а н н ы й метод выделяет текст в составе элемента. Любой символ, введенный поль­
зователем, замещает выделенный текст.

Обработка событий
onblurO
Д а н н ы й метод вызывается при потере элементом фокуса ввода. О б ы ч н о обработ­
чик o n b l u r определяется посредством атрибута o n B l u r , например:
<INPUT TYPE="FILE" . . .
onBlur="doSomeAction()">

onchange()
Для того чтобы метод on c h a n g e получил управление, надо чтобы элемент потерял
фокус ввода после изменения его значения. Данный метод определяется посред­
ством атрибута o n C h a n g e .
1190 Глава 25. Краткое руководство по JavaScript

onfocusO
Метод o n f o c u s , определяемый посредством атрибута o n F o c u s , вызывается при
получении элементом фокуса ввода.

25.8. Объект Form


Объект Form соответствует HTML-элементу Form. Доступ к элементам Form обес­
печивает массив d o c u m e n t . f o r m s . Если ф о р м е присвоено имя, к ней можно обра­
щаться с помощью выражения d o c u m e n t . f ormName, где f ormName — имя формы.

Свойства
action
Значением этого свойства является строка, содержащая URL, по которому должны
быть переданы данные ф о р м ы . Свойство a c t i o n доступно как для чтения, так и
для записи.

elements
Свойство e l e m e n t s обеспечивает доступ к массиву, состоящему из объектов E l e ­
m e n t . Каждый элемент массива соответствует интерфейсному элементу, содержа­
щемуся в HTML-форме. Объекты E l e m e n t рассматривались в разделе 25.6.

encoding
Данное свойство содержит строку, определяющую метод кодирования формы.
Первоначально значение свойства устанавливается с помощью атрибута ENCTYPE.
Свойство e n c o d i n g доступно как для чтения, так и для записи.

method
Данное свойство, первоначально устанавливаемое посредством атрибута METHOD,
может иметь значение либо g e t , либо p o s t . Свойство m e t h o d допускает как чте­
ние, так и запись.

target
Свойство t a r g e t содержит строку, определяющую ф р е й м , в котором должны
отображаться результаты, полученные при активизации ф о р м ы . Это свойство по­
зволяет читать и записывать значение и первоначально определяется посредст­
вом атрибута TARGET.

Методы
reset()
Данный метод вызывает обработчик события o n r e s e t , после чего, если обработ­
чик возвращает значение t r u e , восстанавливает начальное состояние элементов
формы, определенное в документе. Данный метод выполняет те же действия, ко­
торые выполняются после щелчка на кнопке RESET.
25.9. Объект Function 1191

submit()
Данный метод передает данные ф о р м ы на сервер, не вызывая перед этим обработ­
чик o n s u b m i t .

Обработчики событий
onreset()
Данный метод вызывается после щелчка на кнопке RESET либо при выполнении
метода r e s e t . Обычно обработчик o n r e s e t определяется посредством атрибута
onReset.
<FORM A C T I O N = " . . . " ...
onReset="return(maybeReset())">

onsubmit()
Обработчик o n s u b m i t вызывается при активизации пользователем кнопки SUBMIT.
П р и выполнении метода s u b m i t автоматический вызов o n s u b m i t не производится.
Данный обработчик определяется посредством атрибута o n S u b m i t .
<FORM A C T I O N = " . . . " . . .
onSubmit="return(validateEntries ())">
П р и м е р использования o n s u b m i t см. в разделе 24.6.

25.9. Объект Function


Объект F u n c t i o n соответствует функции JavaScript.

Конструктор
n e w Function(argOName,... , a r g N N a m e , bodyString)
Данный конструктор создает новую функцию. Ниже показаны два способа созда­
ния одной и той же функции, но второй способ может быть использован в другой
программе в процессе выполнения сценария.
function square(х) { return(х * х ) ; }
square = new Function("х", "return(х * х)");

Свойства
arguments
В теле функции это свойство предоставляет массив параметров, заданных при вы­
зове. Благодаря наличию свойства a r g u m e n t s имеется возможность создавать
функции с переменным числом параметров. В следующем примере функция сум­
мирует значения переданных ей параметров:
1192 Глава 25. Краткое руководство по JavaScript

function sum О {
var t o t a l = 0;
f o r ( v a r i=0; i < a r g u m e n t s . l e n g t h ; i++) {
t o t a l = t o t a l + arguments[i];
}
return(total) ;
}

arity
Данное свойство, предназначенное только для чтения, определяет число пара­
метров, объявленных для данной функции. Реальное число параметров может от­
личаться от значения свойства a r i t y , в этом случае можно воспользоваться свой­
ством arguments . l e n g t h .

caller
В теле функции это свойство позволяет определить объект Function, из которого
данная функция была вызвана. Если вызов осуществлялся непосредственно из
сценария, свойство c a l l e r содержит значение n u l l . Данное свойство предназна­
чено только для чтения.

prototype
В конструкторе p r o t o t y p e определяет свойства, совместно используемые всеми
объектами указанного типа. Пример использования свойства p r o t o t y p e см. в раз­
деле 24.3.

Методы
Объект F u n c t i o n содержит только те методы, которые присутствуют в каждом
объекте Obj e c t .

Обработчики событий
Обработчики событий отсутствуют. Объект F u n c t i o n не соответствует ни одному
из HTML-элементов.

25.10. Объект Hidden


Объект Hidden соответствует скрытому элементу формы, созданному с помощью де­
скриптора <INPUT TYPE="HIDDEN" . . . >. Объекты данного типа доступны посредст­
вом массива e l e m e n t s объекта Form. Если скрытому элементу присвоено имя, обра­
титься к нему можно с помощью выражения document. formName . elementName, где
formName — имя формы, а elementName — имя элемента.
2 5 . 1 1 . Объект History 1193

Свойства
form
Указанное свойство, предназначенное только для чтения, ссылается на объект
Form, в котором содержится данный элемент.

name
Данное свойство, предназначенное только для чтения, содержит имя элемента,
определенное посредством атрибута NAME.

type
Свойство t y p e доступно только для чтения и содержит значение h i d d e n .

value
Это свойство содержит строку, которая вместе с именем передается программе на
стороне сервера.

Методы
Методы отсутствуют.

Обработчики событий
Обработчики событий отсутствуют.

2 5 . 1 1 . Объект History
Объект H i s t o r y соответствует списку предыстории окна или фрейма. В нем со­
держится список JavaScript, которые ранее посещались данным клиентом. Объект
H i s t o r y доступен посредством свойства h i s t o r y объекта Window, т.е для обраще­
ния к нему используется выражение w i n d o w , h i s t o r y или h i s t o r y .

Свойства
current
JavaScript-сценарий позволяет получить URL текущего документа. Свойство c u r ­
r e n t содержит строку символов и предназначено только для чтения.

length
Данное свойство предназначено только для чтения и сообщает число URL, содер­
жащихся в списке предыстории.

next
Строка, являющаяся значением свойства n e x t , содержит URL следующего доку­
мента в списке предыстории. Данное свойство предназначено только для чтения.
1194 Глава 25. Краткое руководство по JavaScript

previous
Значением свойства p r e v i o u s является строка символов, которая определяет
URL предыдущего документа в списке предыстории. Данное свойство предназна­
чено только для чтения.

Методы
ЬаскО
Д а н н ы й метод дает команду броузеру п е р е й т и на один пункт назад по списку пре­
дыстории.

forwardO
Этот метод дает команду броузеру п е р е й т и на один пункт вперед по списку пре­
дыстории.

go(n)
Данный метод дает команду броузеру перейти на п пунктов вперед (если п положи­
тельное) или на п пунктов назад (если п отрицательное) по списку предыстории.

Обработчики событий
Обработчики событий отсутствуют. Объект H i s t o r y не соответствует ни одному
из HTML-элементов.

25.12. Объект Image


Объект Image соответствует изображению, включенному в состав HTML-документа
с помощью дескриптора <IMG SRC=" . . . " . . . >. Объекты Image доступны посредст­
вом массива d o c u m e n t . i m a g e s . Гхли изображению присвоено имя, обратиться к нему
можно с помощью выражения d o c u m e n t . imageName, где imageName — имя изображе­
ния. Способность выполнять различные действия с изображениями— существенное
достоинство JavaScript. Примеры работы с изображениями см. в разделе 24.5.

Конструктор
n e w I m a g e ( w i d t h , height)
Данный конструктор создает новый объект I m a g e , описывающий изображение
заданного размера. Объект Image в основном создается для того, чтобы, задавая
значение свойства s r c , выполнить предварительную загрузку изображения и со­
хранить его в кэше броузера. Особенность такого подхода заключается в том, что
после установки значения свойства s e t объект Image больше не используется.
П р и м е р применения конструктора Image см. в разделе 24.5.
2 5 . 1 2 . Объект Image 1195

Свойства
border
Данное свойство задает размер обрамления вокруг изображения, которое создает­
ся тогда, когда изображение оформляется как гипертекстовая ссылка. Значение
свойства b o r d e r , предназначенного только для чтения, определяется посредст­
вом атрибута BORDER элемента IMG.

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

height
Свойство h e i g h t содержит сведения о высоте изображения, заданной посредст­
вом атрибута HEIGHT, либо, если атрибут HEIGHT отсутствует, определенной в
графическом файле. Данное свойство доступно только для чтения.

hspace
Свойство h s p a c e определяет число пустых пикселей слева и справа от изображе­
ния. Данное свойство предназначено только для чтения; значение его задается с
помощью атрибута HSPACE.

lowsrc
Netscape (но не Internet Explorer) поддерживает нестандартный атрибут LOWSRC
элемента IMG, посредством которого задается альтернативное изображение,
предназначенное для вывода на дисплей с малой разрешающей способностью.
Строка, определенная с помощью атрибута LOWSRC, является значением свойства
l o w s r c . Это свойство допускает как чтение, так и запись.

name
Данное свойство, предназначенное только для чтения, содержит имя изображе­
ния, определенное с помощью атрибута NAME.

src
Данное свойство содержит строку, которая представляет URL графического фай­
ла, содержащего изображение. Свойство s r c допускает как чтение, так и запись.

vspace
Свойство v s p a c e , предназначенное только для чтения, определяет число пустых
пикселей сверху и снизу изображения. Значение свойства задается с помощью ат­
рибута VSPACE.

width
Свойство w i d t h содержит значение ш и р и н ы изображения, заданной посредством
атрибута WIDTH, либо, если атрибут WIDTH отсутствует, определенной в графиче­
ском файле. Данное свойство доступно только для чтения.
1196 Глава 25. Краткое руководство по JavaScript

Методы
Методы отсутствуют.

Обработчики событий
onabort()
Этот метод вызывается в том случае, если пользователь останавливает загрузку
изображения, щелкая на кнопке S t o p либо активизируя гипертекстовую ссылку,
указывающую на другую Web-страницу. О б ы ч н о метод o n a b o r t определяется по­
средством атрибута o n A b o r t , как показано ниже.
<IMG S R C = " . . . " ...
onAbort="takeSomeAction()">

onerror()
Метод o n e г г o r получает управление, если графический файл, содержащий изо­
бражение, не найден либо если он имеет некорректный формат. О б ы ч н о метод
o n e г г o r определяется посредством атрибута о п Е г г о г .
<IMG S R C = " . . . " . . .
onError="alertС Error loading image')">
Значение n u l l атрибута o n E r r o r подавляет вывод сообщения об ошибке.
<IMG SRC="..." ...
onError ="null">

onload()
Метод o n l o a d вызывается тогда, когда броузер оканчивает загрузку изображения.
Каждое изменение свойства s r c приводит к вызову этого метода. О б ы ч н о метод
o n l o a d определяется посредством атрибута onLoad.
<IMG S R C = " . . . " ...
onLoad="startImageAnimation()">
В данном примере функция s t a r t l m a g e A n i m a t i o n может изменять значение свойст­
ва s r c , что, в свою очередь, приведет к повторному вызову s t a r t l m a g e A n i m a t i o n .

25.13. Объект JavaObject


Объект J a v a O b j e c t — это JavaScript-представление реального объекта Java (аплета)
либо объекта из массива d o c u m e n t . e m b e d s , интерпретируемого как Java-объект. Дан­
ный объект не содержит методов или свойств, но вы можете использовать конструкцию
f o r / i n для работы со свойствами конкретного объекта. Данная возможность, назы-
ваемгш отражением (reflection), доступна в Java 1.1 и более поздних версиях.
2 5 . 1 4 . Объект JavaPackage 1197

25.14. Объект JavaPackage


Объекты типа J a v a P a c k a g e доступны посредством свойств J a v a , n e t s c a p e , s u n
и P a c k a g e s объекта Window. Они используются для обращения к Java-объектам; к
примеру, вы можете таким образом вызывать J a v a . l a n g . S y s t e m . g e t P r o p e r t y .
Соответствующий п р и м е р см. в разделе 24.9.

25.15. Объект Layer


Netscape 4.0 поддерживает слои — отдельные, возможно, перекрывающиеся облас­
ти. Для определения слоев могут использоваться элементы LAYER и ILAYER, а также
средства каскадных листов стилей. П р и м е р ы работы со слоями см. в разделе 24.5. За­
метьте, что в Netscape 6.0 для обеспечения соответствия спецификации HTML 4.0
элементы LAYER и ILAYER не поддерживаются.

Конструкторы
n e w Layer(width)
Данный конструктор создает новый объект L a y e r . Для определения содержимого
надо установить свойство s r c либо использовать метод l o a d .

n e w Layer(width, parentLayer)
Данный конструктор создает слой, дочерний по о т н о ш е н и ю к указанному.

Свойства
above
Данное свойство, предназначенное только для чтения, определяет слой над теку­
щим слоем.

background
Указанное свойство задает изображение для фонового слоя. Свойство b a c k g r o u n d
допускает как чтение, так и запись. Например:
someLayer.background.src = "bricks.gif";

below
Данное свойство, предназначенное только для чтения, определяет слой под теку­
щим слоем.

bgColor
Обычно слои прозрачны, но с помощью свойства b g C o l o r , допускающего как
чтение, так и запись, их можно сделать непрозрачными. Например:
someLayer.bgColor = " b l u e " ;
1198 Глава 25. Краткое руководство по JavaScript

anotherLayer.bgcolor = "#FFOOFF";
thirdLayer.bgColor = null; // прозрачный

clip
Данное свойство, предназначенное как для чтения, так и для записи, определяет
область отсечения. В его состав входят c l i p , t o p , c l i p . b o t t o m , c l i p , l e f t ,
clip.right, clip.width и clip.height.

document
Каждый слой содержит собственный объект Document. Данное свойство, предна­
значенное только для чтения, ссылается на объект документа.

left
Это свойство представляет горизонтальную составляющую позиции слоя относи­
тельно родительского слоя. Свойство l e f t допускает чтение и запись.

name
Свойство name предназначено только для чтения и определяется атрибутом ID
или NAME.

pageX
Данное свойство представляет абсолютную позицию (ее горизонтальную состав­
ляющую) слоя на странице. Свойство радеХ допускает как чтение, так и запись.

pageY
Данное свойство представляет абсолютную позицию (ее вертикальную состав­
ляющую) слоя на странице. Свойство pageY допускает как чтение, так и запись.

parentLayer
Свойство p a r e n t L a y e r содержит данные о родительском слое, если таковой су­
ществует. В противном случае о н о ссылается на объект Window. Д а н н о е свойство
предназначено только для чтения.

siblingAbove
Данное свойство ссылается на слой, расположенный над текущим. Текущий слой и
слой, на который ссылается свойство, должны быть дочерними слоями одного и
того же слоя. Свойство s i b l i n g A b o v e доступно только для чтения.

siblingBelow
Д а н н о е свойство ссылается на слой, расположенный под текущим. Текущий слой и
слой, на который ссылается свойство, должны быть дочерними слоями одного и
того же слоя. Свойство s i b l i n g A b o v e доступно только для чтения.

src
Данное свойство, допускающее как чтение, так и запись, предоставляет URL, ко­
т о р ы й определяет содержимое слоя.
25.15. Объект Layer 1199

top
Данное свойство определяет вертикальную составляющую позиции слоя относи­
тельно родительского слоя. Свойство t o p доступно как для чтения, так и для записи.

visibility
Свойство v i s i b i l i t y определяет видимость слоя. Оно может иметь значения
show (слой является видимым), h i d e , или h i d d e n (слой невидим) и i n h e r i t
(слой наследует видимость родительского слоя). Данное свойство допускает как
чтение, так и запись.

zindex
Данное свойство содержит индекс, определяющий порядок расположения слоя
среди дочерних слоев того же родительского слоя. Слой с наименьшим индексом
является самым нижним, слой с наибольшим индексом — самым верхним. Свойст­
во z i n d e x предназначено как для чтения, так и для записи.

Методы
l o a d ( s o u r c e S t r i n g , width)
Данный метод одновременно изменяет и источник слоя, и его ширину (см. описа­
ние свойства s r c ) .

moveAbove(layer)
Метод moveAbove располагает текущий слой над указанным.

moveBelow(layer)
Метод moveBelow располагает текущий слой под указанным.

m o v e B y ( d x , dy)
Данный метод сдвигает позицию слоя на указанное число пикселей.

m o v e T o ( x , у)
Данный метод размещает слой так, что его верхний левый угол располагается в
указанной позиции включающего слоя или документа. См. свойства l e f t и t o p .

m o v e T o A b s o l u t e ( x , у)
Метод m o v e T o A b s o l u t e размещает слой так, что его верхний левый угол распола­
гается в указанной позиции окна (см. описание свойств радеХ и pageY).

resizeBy(dWidth, d H e i g h t )
Данный метод изменяет ширину и высоту слоя на указанное число пикселей
(см. свойства c l i p . w i d t h и c l i p . h e i g h t ) .
1200 Глава 25. Краткое руководство по JavaScript

resizeTo(width, h e i g h t )
Метод r e s i z e T o устанавливает ширину и высоту слоя в пикселях (см. свойства
clip.width и clip.height).

Обработчики событий
onblurO
Этот метод вызывается тогда, когда слой теряет фокус ввода. Для определения
данного метода используется атрибут o n B l u r дескриптора LAYER или ILAYER. Ес­
ли слой создается средствами каскадных листов стилей, выполняется непосредст­
венное присвоение функции, например:
function blurHandler() { ... }
someLayer.onblur = blurHandler;

onfocus()
Данный метод вызывается при получении слоем фокуса ввода. Для определения
обработчика используется атрибут on F o c u s .

onload()
Метод o n l o a d получает управление по окончании загрузки слоя (это может про­
изойти до того, как слой отобразится на экране). Для определения обработчика
используется атрибут o n L o a d .

onmouseout()
Метод o n m o u s e o u t вызывается тогда, когда курсор мыши выходит за пределы
слоя. Данный метод определяется посредством атрибута onMouseOut.

onmouseoverO
Метод o n m o u s e o v e r вызывается тогда, когда курсор мыши попадает в пределы
слоя. Для определения метода используется атрибут o n M o u s e O v e r .

25.16. Объект Link


Объект L i n k соответствует гипертекстовой ссылке, созданной с помощью деск­
риптора <А HREF=. . .>. На платформах, отличных от Windows, использование кар­
ты изображения на стороне клиента также приводит к созданию объекта L i n k . Для
дост)^па к этим объектам используется массив d o c u m e n t . l i n k s . Ссылки нельзя име­
новать посредством атрибута NAME, поскольку дескриптор <А> с атрибутом NAME по­
мечает позицию в документе.
25.16. Объект Link 1201

Свойства
hash
Данное свойство представляет часть ссылки, указывающую на позицию внутри до­
кумента (включая символ "#"). Свойство h a s h допускает как чтение, так и запись.

host
Указанное свойство предназначено для чтения и записи и содержит строку в фор­
мате гсмя_узла: порт.

hostname
Свойство hostname допускает как чтение, так и запись и содержит имя узла.

href
Данное свойство содержит полный URL и доступно как для чтения, так и для записи.

pathname
Свойство pathname содержит часть URL, расположенную после имени узла и но­
мера порта. Данное свойство допускает как чтение, так и запись.

port
Данное свойство определяет порт, однако содержит не целое число, а строку сим­
волов. Свойство p o r t доступно как для чтения, так и для записи.

protocol
Данное свойство, допускающее как чтение, так и запись, определяет протокол.
Двоеточие входит в состав значения свойства.

search
Свойство s e a r c h содержит информацию, которую принято называть данными
поиска (например, "?х,у" для ISMAP или "?х=1&у=2" при передаче данных фор­
мы на сервер). Данное свойство предназначено как для чтения, так и для записи.

target
Свойство t a r g e t содержит имя, указанное в качестве значения атрибута TARGET.
Это свойство доступно как для чтения, так и для записи. Так, например, для того,
чтобы организовать вывод данных, полученных при активизации любой гипер­
текстовой ссылки в фрейм с именем framel, можно использовать следующий
фрагмент кода:
for(var i=0; i<document.links.length; i++) {
document.links[i].target = "framel";

}
1202 Глава 25. Краткое руководство по JavaScript

Методы
Методы, отличные от обработчиков событий, отсутствуют.

Обработчики событий
onclickO
Этот обработчик вызывается после щелчка пользователя на гипертекстовой ссылке.
Если данный метод возвращает значение f a l s e , переход по ссылке не происходит.
Для определения метода o n c l i c k используется атрибут onClick, например:
<А H R E F = " . . . " ...
onClick="return(maybeCancel() ) ">

ondblclick()
Данный метод вызывается после двойного щелчка мышью. После первого щелчка
вызывается метод o n c l i c k . Метод o n d b l c l i c k не поддерживается Macintosh и
Netscape 6.

onmouseout()
Этот метод получает управление тогда, когда пользователь убирает курсор мыши с
гипертекстовой ссылки. В сочетании с onmouseover метод onmouseout предос­
тавляет возможность подсветки изображения, используемого в качестве гипертек­
стовой ссылки, при помещении на него курсора мыши. Соответствующий пример
см. в разделе 24.5. Для определения метода onmouseout используется атрибут
onMouseOut.

onmouseover()
Данный метод вызывается тогда, когда пользователь помещает курсор мыши на
гипертекстовую ссылку. Для определения onmouseover используется атрибут
onMouseOver. Если метод onMouseOver возвращает значение t r u e , броузер не
отображает URL в строке состояния. Эту возможность можно использовать для
вывода в строке состояния произвольных сообщений.

25.17. Объект Location


Объект L o c a t i o n соответствует текущему URL и доступен посредством свойства
window.location.

Свойства
hash
Данное свойство содержит часть ссылки, указывающую на позицию внутри доку­
мента (включая символ "#"). Свойство hash допускает как чтение, так и запись.
25.17. Объект Location 1203

host
Указанное свойство предназначено для чтения и записи и содержит строку в фор­
мате имя_узла: порт.

hostname
Свойство hostname допускает как чтение, так и запись и содержит имя узла.

href
Данное свойство содержит полный URL и доступно как для чтения, так и для записи.

pathname
Свойство pathname содержит часть URL, расположенную после имени узла и но­
мера порта. Данное свойство допускает как чтение, так и запись.

port
Данное свойство определяет порт, однако содержит не целое число, а строку сим­
волов. Свойство p o r t доступно как для чтения, так и для записи,

protocol
Данное свойство, допускающее как чтение, так и запись, определяет протокол.
Двоеточие входит в состав значения свойства.

search
Свойство s e a r c h содержит информацию, которую принято называть данными
поиска (например, "?х,у" для ISMAP или "?х=1&:у=2" при передаче данных формы
на сервер). Данное свойство предназначено как для чтения, так и для записи.
Свойство s e a r c h можно использовать для реализации карты изображения или
CGI-формы, обеспечивающей навигацию внутри страницы. Для этого надо задать
в качестве целевого URL текущий документ и проверять в начале документа
l o c a t i o n . s e a r c h (функция u n e s c a p e описана в разделе 25.31).

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

replace(newURL)
Этот метод замещает текущий документ новым, определенным с помощью пара­
метра newURL. При этом новый пункт в списке предыстории не создается.
1204 Глава 25. Краткое руководство по JavaScript

Обработчики событий
Обработчики событий отсутствуют.

25.18. Объект Math


В соответствие объекту M a t h не ставится ни один HTML-элемент. Этот объект ис­
пользуется для выполнения основных арифметических операций. Объект M a t h пре­
доставляет практически тот же набор методов, что и Java-класс J a v a . l a n g . M a t h .
Объект M a t h не создается, вместо этого методы и свойства вызываются посредством
выражений M a t h . p r o p e r t y N a m e и M a t h . m e t h o d N a m e { . . . ) , где p r o p e r t y N a m e —
имя свойства, a m e t h o d N a m e — имя метода.

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

Е
Основание натурального логарифма.

LN10
Данное свойство содержит In(10) или log^(lO).

LN2
Данное свойство содержит 1п(2), или log^(2).

LOG10E
Данное свойство содержит logio(e).

LOG2E
Данное свойство содержит lg(e) или log2(e).

PI
Данное свойство содержит число п.

SQRT1_2
Данное свойство содержит квадратный корень из 1/2.

SQRT2
Данное свойство содержит квадратный корень из 2.
2 5 . 1 8 . Объект Math 1205

Методы

Методы общего назначения


abs(num)
Данный метод возвращает абсолютное значение указанного числа (num).

ceil(num)
Метод c e i l возвращает наименьшее целое число, равное или превышающее ука­
занное число (num).

exp(num)
^ num
Д
анныи метод возвращает результат вычисления е
floor(num)
Метод c e i l возвращает наибольшее целое число, равное или меньшее, чем ука­
занное число (num).

log(num)
Данный метод возвращает натуральный логарифм указанного числа (num).
JavaScript не предоставляет методы для вычисления логарифмов по другим осно­
ваниям (например, 10 и 2). Для этого можно воспользоваться соотношением
1оды(п) = 1одь2(п) / 1одь2(Ы)
Функция, предназначенная для преобразования логарифмов, имеет следующий вид:
function log(num, base) {
return(Math.log(num) / Math.log(base));
}
max(numl, num2)
Указанный метод возвращает наибольшее из чисел numl и num2.

min(numl, num2)
Указанный метод возвращает наименьшее из чисел numl и num2.

pow(base, e x p o n e n t )
-. «• , exponent
Метод pow возвращает значение b a s e

random()
Метод r a n d o m возвращает случайное значение в интервале от 0.0 до 1.0 (число 0.0
входит, а число 1.0 не входит в состав интервала).
1206 Глава 2 5 . Краткое руководство по JavaScript

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

sqrt(num)
Метод s q r t возвращает квадратный корень из числа num. Если в качестве пара­
метра задано отрицательное число, возвращается значение NaN.

Тригонометрические методы
П р и вызове всех приведенных ниже методов параметры задаются не в градусах, а
в радианах. Преобразовать градусы в радианы можно следующим образом:
function degreesToRadians(degrees) {
return(degrees * Math.PI / 180);
}

acos(num)
Д а н н ы й метод возвращает арккосинус числа, указанного посредством параметра
num. Результат представлен в радианах.

asin(num)
Данный метод возвращает арксинус числа, }тсазанного посредством параметра num.

atan(num)
Данньп1 метод возвращает арктангенс указанного числа.

atan2(y, х)
Метод a t a n 2 возвращает компонент 0 полярных координат ( г , Э) точки, соот­
ветствующей точке ( х , у) в декартовой системе координат. Это значение равно
арктангенсу у / х и находится в диапазоне от -п до тт.

cos(radians)
Д а н н ы й метод возвращает косинус угла, заданного в радианах.

sin(radians)
Данный метод возвращает синус угла, заданного в радианах.

tan(radians)
Д а н н ы й метод возвращает тангенс угла, заданного в радианах.

Обработчики событий
Обработчики событий отсутствуют.
2 5 . 1 9 . Объект MimeType 1207

2 5 . 1 9 . Объект MimeType
Объект MimeType описывает MIME-тип. В массиве n a v i g a t o r . m i m e T y p e s пере­
числены все МШЕ-типы, поддерживаемые броузером, либо посредством встроенных
модулей, либо с помощью внешних приложений. Н а п р и м е р , следующий фрагмент
кода включает в документ ссылку на файл в формате Adobe Acrobat только в том слу­
чае, если Acrobat поддерживается броузером. В противном случае в документ поме­
щается ссылка, указывающая на о б ы ч н ы й текстовый документ.
d o c u m e n t . w r i t e l n ( ' F o r more i n f o r m a t i o n , s e e ' ) ;
i f ( n a v i g a t o r . m i m e T y p e s [ " a p p l i c a t i o n / p d f " ] != n u l l ) {
document.writeln
('<A H R E F = " m a n u a l . p d f " > t h e w i d g e t m a n u a l < / A > . ' ) ;
} else {
document.writeln
(*<A H R E F = " m a n u a l . t e x t " > t h e w i d g e t m a n u a l < / A > . ' ) ;
}
Список часто используемых MIME-типов см. в табл. 19.1.

Свойства
description
Данное свойство, предназначенное только для чтения, содержит текст, описы­
вающий МШЕ-тип.

enabledPlugin
Свойство e n a b l e d P l u g i n ссылается на доступный объект P l u g i n , поддержи­
вающий МХМЕ-тип. Если дополнительный модуль для поддержки данного типа не
установлен или использование его запрещено, свойство e n a b l e d P l u g i n содер­
жит значение n u l l . Д а н н о е свойство предназначено только для чтения.

suffixes
Данное свойство содержит список расширений файлов, связанных с МШЕ-типом.
Элементы списка разделяются запятыми. Свойство s u f f i x e s доступно только для
чтения.

type
Значением данного свойства является строка, содержащая тип, например
application/postscript.

Методы
Методы отсутствуют.

Обработчики событий
Обработчики событий отсутствуют. Объект MimeType не соответствует ни одному
из HTML-элементов.
1208 Глава 25. Краткое руководство по JavaScript

2 5 . 2 0 . Объект Navigator
Объект N a v i g a t o r предоставляет и н ф о р м а ц и ю о броузере. Для доступа к этому
объекту используется свойство n a v i g a t o r объекта Window, т.е. обращение к объекту
N a v i g a t o r имеет вид w i n d o w . n a v i g a t o r либо n a v i g a t o r .

Свойства
Н и ж е описаны свойства объекта N a v i g a t o r . В листинге 25.2 приведен код доку­
мента, который строит таблицу, содержащую некоторые свойства. Представление
документа в различных броузерах показано на рис. 25.2 и 25.3.

appCodeName
Д а н н о е свойство, предназначенное только для чтения, содержит кодовое имя бро­
узера. Для Internet Explorer и Netscape это свойство имеет значение M o z i l l a .

appName
Значением этого свойства является имя броузера, например Netscape или Micro­
soft Internet Explorer. Свойство appName доступно только для чтения.

appVersion
Свойство a p p V e r s i o n , доступное только для чтения, предоставляет сведения об
используемой операционной системе, включгш номер реализации.

language
Свойство l a n g u a g e , предназначенное только для чтения, содержит сведения о
языке броузера. Для английской версии свойство имеет значение e n .

mimeTypes
Д а н н о е свойство предоставляет доступ к массиву объектов MimeType, поддержи­
ваемых броузером, либо посредством встроенных модулей, либо с помощью
внешних приложений (см. раздел 25.19).

platform
Свойство p l a t f o r m предоставляет сведения о типе компьютера, для которого
броузер был скомпилирован. Н а п р и м е р , для Windows 95, 98 и N T данное свойство
имеет значение Win32. Свойство p l a t f o r m предназначено только для чтения.

plugins
Д а н н о е свойство предоставляет массив объектов P l u g i n , поддерживаемых бро­
узером. Соответствующие п р и м е р ы см. в разделе 24.4.
2 5 . 2 0 . Объект Navigator 1209

userAgent
Данное свойство содержит строку, передаваемую броузером серверу в поле U s e r -
A g e n t заголовка запроса. Свойство u s e r A g e n t доступно только для чтения.
На рис. 25.2 и 25.3 приведены п р и м е р ы некоторых из этих свойств. Для отобра­
жения свойств используется документ, код которого представлен в листинге 25.2.

The Navigd*o» 0,Ы««Л;^'1*в«*с#е;^^^^^^^^^^^^ BTiorxl


i £Эе i-M Vtevv Q^o £QfWntracatt» НФ

'' . .. 3 <-^r ^ ^ .,s r£ Э t si\


The Navigator Object
1 Property Value
1 appCodeName Mozilla
1 appName Netscape
appVersion 4 7 [en] (Wm98, Г)
userAgent Mozua/A 7 [en] (Win98,1) Рис. 25.2. Свойства объекта Navigator,
отображаемые в броузере Netscape 4.7,
j^*i ::я;]}гг Document Done который выполняется в среде Windows 98

Щ The Navigatof O b ^ t Micfosofl InleinM Е)1|йвг|я


File %^in View FgYorftes Tods fc^dp

~3
The Navigator Object

Property Value
appCodeName Mozilla
appName Microsoft Internet Explorer
app Version 4 0 (compatible; MSIE 5 0. Windows 98. DigExt) Рис. 25.3. Свойства объекта
userAgent MoaIIa/4.0 (compatible, MSIE 5 0, Windows 98, DigExt) Navigator, отображаемые в броузере
J Internet Explorer 5.0, который
<Э] Done i-jMyConnpirfef выполняется в среде Windows 98

Листинг 2 5 . 2 . N a v i g a t o r . htшll

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>The N a v i g a t o r O b j e c t < / T I T L E >

<SCRIPT T Y P E = " t e x t / j a v a s c r i p t " >


<! —
function makePropertyTable(паше, object, p r o p e r t y L i s t ) {
d o c u m e n t . w r i t e l n ( " < H 2 > " + name + " < / H 2 > " ) ;
d o c u m e n t . w r i t e l n ( " < T A B L E BORDER=l>\n" +
" <TR><TH>Property<TH>Value");
var propertyName;
f o r ( v a r i = 0 ; i < p r o p e r t y L i s t . l e n g t h ; i4-+) {
propertyName = p r o p e r t y L i s t [ i ] ;
document . w r i t e l n C <TR><TD>" + p r o p e r t y N a m e +
1210 Глава 25. Краткое руководство по JavaScript

"<TD>" + object[propertyName]);
}
document.writeln("</TABLE>");
}
// — >
</SCRIPT>
</HEAD>

<BODY BGCOLOR="WHITE">

<SCRIPT TYPE="text/javascript">
<! —

var propNames = new Array("appCodeName", "appName",


"appVersion", "userAgent");
makePropertyTable("The Navigator Object", navigator, propNames);

// — >
</SCRIPT>

</BODY>
</HTML>

Методы
javaEnabledO
Данный метод возвращает t r u e , если броузер поддерживает Java и обработка Java-
кода разрешена. В противном случае возвращается значение f a l s e .

taint£nabled()
Данный метод возвращает t r u e , если установлена переменная окружения
NSENABLETAINT. Данная переменная позволяет JavaScript-сценарию в одном
документе обрабатывать привилегированные данные других документов. Netscape
удалила эту возможность в JavaScript 1.2 и заменила ее механизмом подписанных
сценариев. Информацию о защите в JavaScript вы найдете по адресу
h t t p : / / d e v e l o p e r . n e t s c a p e . c o m / d o c s / m a n u a l s / j s / c l i e n t / j sguide/sec.htm.

Обработчики событий
Обработчики событий отсутствуют. Объект N a v i g a t o r не соответствует ни одно­
му из HTML-элементов.

2 5 . 2 1 . Объект Number
Объект Number содержит информацию, связанную с использованием чисел в сце­
нариях. Для обращения к свойствам нет необходимости создавать объект этого типа.
Доступ осуществляется с помощью выражения Number .propertyName, где p r o p e r t y -
2 5 . 2 1 . Объект Number 1211

Name — имя свойства. Объект Number создается только тогда, когда надо использовать
метод t o S t r i n g , позволяющий задавать основание системы счисления.

Конструкторы
new Number(value)
Данный конструктор создает объект Number для простого значения, заданного с
помощью параметра v a l u e .

Свойства
MAX_VALUE
Свойство MAX_VALUE представляет максимальное число, поддерживаемое JavaScript.

MIN_VALUE
Свойство MIN_VALUE представляет минимальное число, поддерживаемое JavaScript.

NaN
Данное свойство представляет специальное значение NaN (not-a-number — не число).
Для проверки на наличие данного значения используется функция isNaN. Приме­
нять для проверки оператор == нельзя, поскольку при сравнении NaN с любым чис­
лом (в том числе при выполнении выражения Number. NaN ==-• Number. NaN) воз­
вращается значение f a l s e .

NEGATIVE J N F I N I T Y
Данное свойство представляет отрицательное значение, соответствующее пере­
полнению. Например, это значение получится, если умножить
Number .MAXVALUE на -2. Если умножить NEGATIVE_INFINITY на любое число,
результат будет равен NEGATIVEINFINITY. В результате деления любого числа на
NEGATIVEINFINITY получается значение 0. Ч а с т н о е от деления двух значений
NEGATIVE_INFINITY равно NaN.

POSITIVE J N F I N I T Y
Данное свойство представляет положительное значение, соответствующее пере­
полнению.

Методы
toStringO
Данный метод дает те же результаты, что и вызов t o S t r i n g {10).

toString(radix)
Данный метод преобразует число в строку символов, используя систему счисле­
ния, заданную посредством параметра r a d i x . Н а п р и м е р , код, приведенный в лис-
1212 Глава 25. Краткое руководство по JavaScript

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


счисления. Внешний вид документа в окне броузера Netscape 4.7, выполняющегося
в операционной системе Windows 98, показан на рис. 25.4.

Листинг 25.3. NumberToString. html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Converting Numbers to Strings</TITLE>

<SCRIPT TYPE="text/javascript">
<! —

function makeNumberTable(numberList, radixList) {


document.write("<TABLE BORDER=l>\n<TR>");
for(var i=0; i<radixList.length; i++) {
document.write("<TH>Base " + radixList[i]);
}
var num;
for(var i=0; i<numberList.length; i++) {
document.write("\n<TR>");
num = new Number(numberList[i]);
for(var j=0; j<radixList.length; j++) {
document.write("<TD>" + num.toString(radixList[j]));
}
}
document.writeln("\n</TABLE>");
}
// — >
</SCRIPT>
</HEAD>

<BODY BGCOLOR="WHITE">
<Hl>Converting Numbers to Strings</Hl>

<SCRIPT TYPE="text/javascript">
<! —

var nums = new Array(0, 1, 2, A, 5, 6, 7, 8, 9, 10, 15, 100,


512, 1000);
var radixes = new Array(10, 2, 8, 16);

makeNumberTable(nums, radixes);

// — >
</SCRIPT>

</BODY>
</HTML>
2 5 . 2 2 . Объект Object 1213

ЕйакшЗ! ifting Numbets to Strmm -Hei««^im:::::::r:,:; ::-•::• ::%;?:•::;>

£ве £ci yiew go Comrnuracetoi Help


4' ".'" 5 5'^ ^ ^ i- у J -C Щ
Convertin g Numbers to Strings
Base 10 Basel Base 8 Base 16
0 0 0 0

1 ^ Л 1 '^ 1
2 2
10
2
4 100 4
5 101 5 ^
'5 11
6 no 6
7 111 7 7^ 11
8 1000 10 8 1
9 ;iooi 11 5 1
10 1010 'l2 a 1
15 nil 17 f 1
100 nooioo 144 64 1
512 ЛООООООООО 1000 200 1
1000 i n n O l O O O 1750 3e8 1 Рис. 25.4. Объект Number позволяет
представлять числа в различных
t^f-^ DocumenJ Done ;^ ; •Ш-.:::.: йjr^:! :Ш.;..:: ^j<^:j:' .ij системах счисления

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

Обработчики событий
Обработчики событий отсутствуют. Объект Number не соответствует ни одному из
HTML-элементов.

2 5 . 2 2 . Объект Object
Ob j e c t — это объект, на базе которого созданы все остальные объекты. Свойства и
методы объекта Ob j e c t содержат все объекты JavaScript.

Конструкторы
n e w ObjectO
Данный конструктор создает универсальный объект Ob j e c t .

n e w Object(primitiveValue)
В зависимости от типа параметра данный конструктор создает объект Number,
S t r i n g , B o o l e a n или F u n c t i o n .

{propl:vall, prop2:val2,..., propNrvalN)


Объекты можно также создавать, используя "литеральные" обозначения.
1214 Глава 25. Краткое руководство по JavaScript

Свойства
constructor
Данное свойство, предназначенное только для чтения, ссылается на JavaScript-
функцию, посредством которой был создан экземпляр объекта.

prototype
Свойство p r o t o t y p e по сути не является свойством O b j e c t , а, скорее, свойством,
предназначенным для использования в любом конструкторе. В частности, оно
применяется для инициализации свойств объектов, определенных пользователем.
О данном свойстве см. также в разделе 24.3.

Методы
assign(value)
Данный метод вызывается тогда, когда объект определенного вами типа присутст­
вует в левой части о п е р а т о р а прямого присваивания. В большинстве случаев метод
a s s i g n создает новый объект и непосредственно заполняет его поля.

eval(javaScriptCode)
Данный метод получает произвольную строку и вычисляет результат.

toStringO
Метод t o S t r i n g генерирует строку, соответствующую объекту.

valueOfO
Если простое значение, соответствующее объекту, существует, данный метод воз­
вращает это значение (см. раздел 15.21).

Обработчики событий
Обработчики событий отсутствуют.

25.23. Объект Option


Объект O p t i o n представляет пункт OPTION в составе элемента SELECT. Для обра­
щения к данному объекту используется массив o p t i o n s объекта S e l e c t , который, в
свою очередь, доступен посредством массива e l e m e n t s объекта Form.

Конструкторы
n e w Option()
new Option(text)
n e w O p t i o n ( t e x t , value)
n e w O p t i o n ( t e x t , value, d e f a u l t S e l e c t e d )
n e w O p t i o n ( t e x t , value, d e f a u l t S e l e c t e d , s e l e c t e d )
Каждый из приведенных конструкторов создает новый объект O p t i o n , который
можно включить в состав объекта S e l e c t , разместив его в конце массива o p t i o n s .
25.24. Объект Password 1215

Свойства
defaultSelected
Данное свойство содержит логическое значение, которое определяет, должен ли
объект O p t i o n быть выбран по умолчанию. Свойство d e f a u l t S e l e c t e d задается
посредством атрибута SELECTED и предназначено только для чтения.

index
Это свойство, предназначенное только для чтения, определяет позицию объекта
O p t i o n в массиве o p t i o n s объекта S e l e c t .

selected
Данное свойство содержит логическое значение, указывающее, выбран ли в дан­
ный момент пункт, соответствующий объекту. Свойство s e l e c t e d доступно как
для чтения, так и для записи.

text
Свойство t e x t содержит текст, соответствующий пункту элемента SELECT. Дан­
ное свойство допускает как чтение, так и запись.

value
Д а н н о е свойство содержит значение, которое при активизации ф о р м ы вместе с
именем объекта S e l e c t передается программе на стороне сервера. Первоначаль­
но это свойство задается посредством атрибута VALUE и доступно как для чтения,
так и для записи.

Методы
Методы отсутствуют.

Обработчики событий
Обработчики событий отсутствуют. Обработчики o n b l u r , o n f o c u s и o n c h a n g e
связываются не с объектами O p t i o n , а с объектом S e l e c t .

25.24. Объект Password


Объект P a s s w o r d соответствует полю ввода пароля, т.е. HTML-элементу, создавае­
мому посредством дескриптора < INPUT TYPE=" PAS SWORD" . . . >. Для обращения к
объекту P a s s w o r d используется массив e l e m e n t s соответствующего объекта Form.
Если элементу присвоено имя, он доступен с помощью выражения d o c u m e n t .
f ormName. passwordName, где f ormName — имя формы, a passwordName — имя эле­
мента.
1216 Глава 25. Краткое руководство по JavaScript

Свойства
defaultValue
Данное свойство, предназначенное только для чтения, содержит значение, пер­
воначально задаваемое посредством атрибута VALUE.

form
Свойство f o r m предоставляет доступ к объекту Form, содержащему поле ввода па­
роля. Это свойство предназначено только для чтения.

name
Свойство name доступно только для чтения и содержит значение атриб)а'а NAME.

type
Данное свойство предназначено только для чтения и содержит значение
password.

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

Методы
blurO
В результате выполнения данного метода элемент теряет фокус ввода.

focus()
в результате выполнения данного метода элемент получает фокус ввода.

select()
Этот метод вызывает выделение текста в составе элемента. Символ, введенный
пользователем, заменяет весь выделенный текст.

Обработчики событий
onblurO
Данный метод вызывается тогда, когда поле ввода пароля т е р я е т фокус ввода.
Этот метод определяется с помощью атрибута o n B l u r .

onchange()
Для того чтобы метод o n c h a n g e получил управление, надо чтобы поле ввода па­
роля потеряло фокус после изменения значения, содержащегося в поле. Данный
метод определяется посредством атрибута o n C h a n g e .
25.25. Объею* Plugin 1217

onfocus()
Д а н н ы й метод вызывается тогда, когда поле ввода пароля получает фокус ввода.
Этот метод определяется с помощью атрибута on F o c u s .

onkeydown()
Этот метод вызывается после первого нажатия пользователем клавиши, когда по­
ле ввода пароля имеет фокус ввода. Если onkeydown возвращает значение f a l s e ,
символ не вводится.

onkeypress()
П р и первом нажатии клавиши пользователем обработчик o n k e y p r e s s вызывает­
ся после onkeydown. П р и последующих нажатиях клавиш o n k e y p r e s s вызывает­
ся, а o n k e y d o w n — нет. Если обработчик o n k e y p r e s s возвращает значение f a l s e ,
символ не вводится.

onkeyupO
Обработчик o n k e y u p вызывается тогда, когда пользователь отпускает клавишу.

25.25. Объект Plugin


В данном разделе описываются дополнительные модули, доступные посредством
массива n a v i g a t o r . p l u g i n s . Следует различать дополнительные модули, установ­
л е н н ы е в броузере, и объекты, включенные в текущий документ; обращение к по­
следним осуществляется через массив e m b e d s объекта Document. P l u g i n — не совсем
о б ы ч н ы е объекты. Они включаются в массив, но каждый элемент массива является
объектом MimeType. П р и м е р использования объектов P l u g i n и связанных с ними
объектов MimeType см. в р^азделе 24.4.

Свойства
description
Д а н н о е свойство, предназначенное только для чтения, содержит текст, описы­
вающий дополнительный модуль.

filename
Значением свойства f i l e n a m e является имя файла, содержащего код дополни­
тельного модуля. Данное свойство доступно только для чтения.

, length
Свойство l e n g t h определяет число объектов MimeType в массиве.

name
Д а н н о е свойство содержит краткое имя дополнительного модуля. Такие имена
можно использовать в качестве индексов при обращении к элементам массива
document.plugins.
1218 Глава 25. Краткое руководство по JavaScript

Методы
Методы отсутствуют.

Обработчики событий
Обработчики событий отсутствуют. Объект P l u g i n не соответствует ни одному из
HTML-элементов.

25.26. Объект Radio


Объекты R a d i o соответствуют переключателям опций, содержащимся в составе
ф о р м ы и создаваемым посредством дескрипторов <INPUT TYPE="RADIO" . . . >.
Доступ к объектам R a d i o осуществляется посредством массива e l e m e n t s соответст­
вующего объекта Form. Если объектам R a d i o и ф о р м е , в которой они содержатся,
присвоены имена, обращение к объекту может производиться с помощью выражения
d o c u m e n t . formName. r a d i o N a m e , где formName— имя ф о р м ы , а r a d i o N a m e — имя
элемента.

Свойства
checked
Данное свойство, содержит логическое значение, определяющее, выбран ли в
данный момент переключатель опции. Свойство c h e c k e d допускает как чтение,
так и запись.

defaultChecked
Свойство d e f a u l t C h e c k e d содержит логическое значение, определяющее, дол­
жен ли переключатель быть выбран первоначально. Данное свойство устанавли­
вается с помощью атрибута CHECKED и доступно только для чтения.

form
Данное свойство, предназначенное только для чтения, ссылается на объект Form,
содержащий переключатель опций.

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

type
Данное свойство содержит значение r a d i o . Поскольку все объекты E l e m e n t
имеют свойство t y p e , оно может быть использовано для идентификации типа
элементов, содержащихся в массиве f o r m . e l e m e n t s . Свойство t y p e доступно
только для чтения.
25.27. Объект RegExp 1219

value
Если переключатель выбран, значение свойства v a l u e передается вместе с име­
нем элемента CGI-программе на стороне сервера. Данное свойство доступно как
для чтения, так и для записи.

Методы
blurO
В результате выполнения данного метода элемент теряет фокус ввода.

clickO
П р и вызове данного метода выполняются те же действия, что и по щелчку мышью
на переключателе, но событие o n C l i c k не генерируется. Выполнение метода
c l i c k дает те же результаты, что и установка свойства c h e c k e d .

focus()
В результате выполнения данного метода элемент получает фокус ввода.

Обработчики событий
onblurO
Обработчик o n b l u r вызывается тогда, когда переключатель т е р я е т фокус ввода.
Этот метод определяется с помощью атрибута o n B l u r , как показано ниже.
<INPUT TYPE="RADIO" . . .
onBlur="doSomeAction()">

onclickO
Данный метод получает управление после щелчка мышью на переключателе оп­
ций, но не в результате выполнения метода c l i c k . Обработчик o n c l i c k опреде­
ляется посредством атрибута o n C l i c k .

onfocus()
Обработчик o n f e c u s вызывается тогда, когда переключатель опции получает фо­
кус ввода. Этот обработчик устанавливается посредством атрибута o n F o c u s .

25.27. Объект RegExp


В Netscape 4.0 был введен объект RegExp, представляющий регулярные выраже­
ния. Кроме того, была реализована поддержка этих выражений объектом S t r i n g по­
средством методов m a t c h , r e p l a c e , s e a r c h и s p l i t .

Конструкторы
n e w RegExpC'pattern")
Д а н н ы й конструктор создает регулярное выражение. Регулярное выражение — это
строка, содержащая н е к о т о р ы е специальные символы, используемые для обозна-
1220 Глава 25. Краткое руководство по JavaScript

чения фрагментов текста (см. табл. 15.1). Наиболее важными из них являются +
(одно или более вхождений предыдущего символа), * (любое, в том числе нулевое
количество вхождений предыдущего символа) и ? (необязательное вхождение
предыдущего символа). П р и отсутствии специальных символов установление со­
ответствия регулярному выражению сводится к обычному сравнению строк. На­
пример, представленное ниже регулярное выражение означает следующее:
"символ 'z', за ним следует один либо более символов 'а', далее символ Ъ ' , сопро­
вождаемый любым (в том числе нулевым) количеством символов 'с', затем необя­
зательный символ 'd', после которого следует символ 'е'".
var re = new RegExp("za+bc*d?e");
В состав объекта RegExp входит метод t e s t , к о т о р ы й читает строку и возвращает
значение t r u e только в том случае, если строка содержит регулярное вырг1жение.
П р и н и м а я во Внимание! приведенное выше определение r e , можно сделать вы­
вод, что в результате следующих вызовов метода t e s t будет получено значение
true:
re.test("zabcde");
re.test("xxxxxzabcdexxxxx");
re.test("zaaaabcde") ;
re.test("zaaaabde") ;
re.test("zaaaabe");
re.test("XXzaabcccccdeYY") ;

n e w RegExpC'pattern", "g")
Д а н н ы й конструктор создает регулярное выражение, предназначенное для гло­
бального поиска в строке. В объекте S t r i n g определен метод m a t c h , который
возвращает массив, содержащий фрагменты текста, соответствующие регулярно­
му выражению. Если параметр "g" не указан, возвращается первый фрагмент, со­
ответствующий выражению. Н и ж е приведен фрагмент кода, демонстрирующий
использование рассматриваемого конструктора и метода m a t c h объекта S t r i n g .
П р и первом вызове метода m a t c h возвращается массив, содержащий последова­
тельность символов " а Ь с " , при втором вызове — массив, содержащий последова­
тельности " а Ь с " и " a b b b b c " .
var str = "abcabbbbcABCABBBBC";
var rel = new RegExp("ab+c");
var re2 = new RegExp ("ab-t-c", " g " ) ;
var resultl = str.match(rel);
var result2 = str.match(re2);

n e w RegExpC'pattern", "i")
Этот конструктор создает регулярное выражение для проверки без учета регистра
символов.

n e w RegExpC'pattern", "gi")
Данный конструктор создает регулярное выражение для глобального поиска без
учета регистра символов. Например, в результате выполнения приведенного ниже
25.27. Объект RegExp 1221

фрагмента кода будет создан массив, содержащий последовательности "аЬс",


"abbbbc", "ABC" и "АВВВВС".
var str = "abcabbbbcABCABBBBC";
var re = new RegExp("ab+c", "gi");
var result = str.match(re);

/pattern/
Указанное выражение представляет собой сокращенную запись конструктора new
RegExp ( " p a t t e r n " ). Например, следующие две строки кода создают одинаковые
регулярные выражения:
var rel = /ab+c/;
var ге2 = new RegExp("ab+c");
Соответствующие примеры будут рассмотрены в разделе 25.31.

/pattern/g
Данное выражение представляет собой сокращенную запись конструктора new
RegExp ( " p a t t e r n " , ' ' g " ) .

/pattern/i
Данное выражение представляет собой сокращенную запись конструктора new
RegExp ( ' ' p a t t e r n " , ' ' i " ) .

/pattern/gi
Данное выражение представляет собой сокращенную запись конструктора new
RegExp ( ' ' p a t t e r n " , " g i " ) .

Свойства
Описанные ниже свойства принадлежат объекту RegExp, а не конкретному регу­
лярному выражению. Для доступа к свойству используется выражение RegExp.
propertyName, где propertyName — имя свойства. Сокращенные имена свойств ($_,
$* и т.д.) привычны для программистов, имеющих опыт работы на языке Perl.

input
$_
Если метод exec или t e s t регулярного выражения вызывается без указания строки,
используется значение данного свойства. При выполнении обработчиков событий
для объектов Text, TextArea, S e l e c t и Link это свойство автоматически заполня­
ется текстом. Рассматриваемое свойство допускает как чтение, так и запись.

lastMatch
$8с
Данное свойство содержит последнюю подстроку, соответствующую регулярному
выражению. Это свойство предназначено только для чтения и получает значение
после вызова метода exec.
1222 Глава 25. Краткое руководство по JavaScript

lastParen
$+
Значением данного свойства, предназначенного только для чтения, является по­
следний фрагмент текста, соответствующий регулярному выражению в скобках.
Это свойство заполняется после вызова метода e x e c .

leftContext

$^
Это свойство содержит часть строки, которая предшествует последнему фрагмен­
ту, соответствующему регулярному выражению. Сам фрагмент в состав данного
свойства не включается. Свойство l e f t C o n t e x t ($ ) предназначено только для
чтения и заполняется после вызова метода e x e c .

multiline
$*
Данное свойство содержит логическое значение, которое определяет, может ли
проверка на соответствие регулярному выражению продолжаться после появле­
ния символа новой строки. Это свойство допускает как чтение, так и запись и
должно быть установлено перед вызовом метода e x e c . П р и вызове обработчиков
событий текстовой области данному свойству автоматически присваивается зна­
чение t r u e .

rightcontext

$'
Это свойство содержит правую часть строки, которая следует после последнего
фрагмента, соответствующего регулярному выражению. Данное свойство доступ­
но только для чтения и заполняется после вызова метода e x e c .
$1
$2

$9
Указанные свойства содержат первые девять фрагментов, соответствующих регу­
л я р н ы м выражениям в скобках. О н и предназначены только для чтения и запол­
няются после вызова метода e x e c .

Методы
Д а н н ы е методы принадлежат не объекту RegExp, а конкретным регулярным вы­
ражениям.

compile(pattern, flags)
Д а н н ы й метод компилирует регулярное выражение для повышения эффективно­
сти работы.
2 5 . 2 7 . Объект RegExp 1223

exec(string)
Метод e x e c п р о в е р я е т указанную строку (параметр s t r i n g ) на соответствие регу­
лярному выражению и заполняет описанные ранее свойства объекта RegExp. П р и
написании сценария вы можете не указывать имя метода, т.е. вызывать
s o m e R e g E x p ( s t r i n g ) вместо s o m e R e g E x p . e x e c ( s t r i n g ) .

exec()
Этот метод выполняет те же действия, что и вызов e x e c ( R e g E x p . i n p u t ) .

test(string)
Данный метод определяет, содержит ли заданная строка (параметр s t r i n g ) регу­
л я р н о е выражение и, в зависимости от результатов проверки, возвращает значе­
ние t r u e или f a l s e . Вызов s o m e R e g E x p . t e s t ( s t r i n g ) дает те же результаты,
что и вызов метода s t r i n g , s e a r c h ( s o m e R e g E x p ) .

Обработчики событий
Обработчики событий отсутствуют. Объект RegExp не соответствует ни одному из
HTML-элементов.

Специальные символы в составе регулярных


выражений
П р и рассмотрении конструкторов RegExp мы ограничивались специальными
символами "+", "*" и "?". В табл. 25.1 представлен полный список специальных симво­
лов и конструкций. Заметьте, что строка не должна соответствовать регулярному вы­
ражению, она должна лишь содержать подстроку, соответствующую этому выраже­
нию. Поэтому, встретив фразу типа "регулярному выражению / а Ь + с / соответствует
строка " a b b b c " , следует иметь в виду, что ему же соответствуют и строки "Xxabbbc",
"XXabbbcYYYY" и т.д.

Таблица 2 5 . 1 . Специальные символы и конструкции, используемые в


регулярных выражениях

Символ или Описание


конструкция

+ Одно или более вхождений предыдущего символа. Например,


выражению / а Ь + с / соответствуют "аЬс" и " a b b b b b c " , но не " а с "
Ноль или более вхождений предыдущего символа. Например,
в ы р а ж е н и ю / а Ь * с / соответствуют " а с " , " а Ь с " и " a b b b b b c "
Необязательное вхождение предыдущего символа. Например,
в ы р а ж е н и ю / а Ь ? с / соответствуют " а с " и "аЬс", но не
"abbbbbc"
1224 Глава 25. Краткое руководство по JavaScript

Продолжение табл. 25.1


Любой символ кроме символа перевода строки. Например, вы­
ражению / а . с / соответствуют " а Ь с " и " a q c " , но не " а с " и не
"abbe"
\ Литеральное представление следующего символа. Если за об­
ратной косой чертой следует специальный символ, его специ­
альное значение отменяется. Например, выражению / а \ * * Ь /
соответствуют "аЬ", " а * Ь " и " а * * * * * Ь "
{выражение) Строка, соответствующая этому выражению, "запоминается" в
свойстве RegExp с именем $А^
р 1 I р2 Означает р 1 или р 2 . Н а п р и м е р , выражению / f с о | b a r / соот­
ветствуют " f o o t b a l l " и " b a r s t o o l "
{п} Ровно п вхождений предыдущего символа. Н а п р и м е р , выраже­
нию / а Ь {3 } с / соответствует " a b b b c " , но не " a b b e " и не
"abbbbc"
{п, } Как минимум п вхождений предыдущего символа. Н а п р и м е р ,
выражению / а Ь { 3 , } с / соответствуют " a b b b c " и " a b b b b c " , но
не " a b b e "
{ n l ^ п2 } Не менее п 1 , но не более п2 вхождений предыдущего символа
[с1е2 . . .en] Один из указанных символов. Например, выражению
/ а [р1] * е / соответствуют "ае", " a p p l e " и " a l l p e " . Для пред­
ставления символов в заданном диапазоне в квадратных скобках
можно указывать знак "-". Н а п р и м е р , выражению [ a - z ] соот­
ветствует любая латинская буква нижнего регистра, выражению
[ 0-7 ] — любая восьмеричная ц и ф р а (цифра в диапазоне от О до
7) и т.д.
[ ^ele2 . . .en] Любой символ, не принадлежащий заданному набору. Напри­
мер, выражению / а ["^pl] * е / соответствуют " а е " и " a q q x x e " ,
но не " a p p l e " и не " a l l p e "
\b, \В Граница слова (\Ь) или ее отсутствие (\В). Н а п р и м е р , выраже­
нию / а \ Ь е / соответствует "а с", но не "аЬе", а в ы р а ж е н и ю
/ а \ В е / — "аЬс", но не "а с"

\w, \W Символ, который может присутствовать в составе имени (\w), и


символ, который не может присутствовать в составе имени (\W).
Конструкция \w эквивалентна [ A - Z a - z 0 - 9 _ ] , а \W— ['^A-Za-
z0-9_]

\ci, \D Ц и ф р а ( \ d ) и символ, не являющийся ц и ф р о й ( \ D ) . Д а н н ы е


конструкции эквивалентны соответственно [ 0 - 9 ] и [ ^ 0 - 9 ]

\f, \п, \г. Обозначают соответственно перевод формата, перевод строки,


\t' ^"^ возврат каретки, табуляцию и вертикальную табуляцию
2 5 . 2 8 . Объе1ст Reset 1225

Окончание табл. 25.1

\ S;, \ S Обозначает пробел или символ, используемый вместо него для раз­
деления слов ( \ s ) , и символ, не являющийся таковым. (\S). Конст­
рукция \ s эквивалентна [ \ f \ n \ r \ t \ v ] , a \ S - - [^\f \ n \ r \ t \ v ]

/ххх/ Символ, который представляется ASCII-кодом ххх

25.28. Объект Reset


Объект R e s e t соответствует кнопке RESET в составе ф о р м ы , создаваемой с помо­
щью дескриптора <INPUT TYPE="RESET ! " . . . > . Для обращения к объекту R e s e t
используется массив e l e m e n t s объекта Form. Если и ф о р м е , и кнопке присвоены
имена, для доступа к объекту R e s e t можно использовать выражение d o c u m e n t .
formName. r e s e t B u t t o n N a m e , где formName— имя ф о р м ы , а r e s e t B u t t o n N a m e —
имя кнопки RESET.

Свойства
form
Данное свойство, предназначенное только для чтения, обеспечивает доступ к объ­
екту Form, содержащему кнопку RESET.

Если в дескрипторе, посредством которого определена кнопка, содержится атри­


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

type
Данное свойство имеет значение r e s e t . Свойство t y p e содержат все объекты
E l e m e n t , поэтому о н о может быть использовано для идентификации типа объек­
та. Данное свойство предназначено только для чтения.

value
Значение свойства v a l u e определяет надпись на кнопке. Свойство v a l u e допус­
кает как чтение, так и запись.

Методы
blurO
В результате выполнения данного метода кнопка RESET теряет фокус ввода.

clickO
В результате вызова данного метода выполняются те же действия, что и после
щелчка на кнопке RESET, однако событие o n C l i c k не генерируется. Вместо мето­
да c l i c k можно вызвать метод r e s e t ф о р м ы .
1226 Глава 25. Краткое руководство по JavaScript

Обработчики событий
onblur()
Д а н н ы й метод вызывается тогда, когда кнопка RESET т е р я е т фокус ввода. Обра­
ботчик o n b l u r задается посредством атрибута o n B l u r .
<INPUT TYPE="RESET" . . .
onBlur="doSomeAction()">

onclick()
Обработчик o n c l i c k вызывается тогда, когда пользователь щелкает мышью на кнопке
RESET. При вызове метода c l i c k обработчик не получает управления. Обычно дан­
ный метод определяется с помощью атрибута o n C l i c k , как показано ниже.
<INPUT TYPE="RESET" . . .
onClick="doSomeAction()">
Если метод o n c l i c k возвращает значение f a l s e , сброс ф о р м ы не производится.
Например:
<INPUT TYPE="RESET" . . .
onClick="return(maybeReset())">

Т о т же э ф ф е к т может быть достигнут с помощью обработчика o n R e s e t ф о р м ы ,


содержащей кнопку.

ondblclickO
Этот метод вызывается после двойного щелчка мышью на кнопке. После первого
щелчка вызывается метод o n c l i c k . Обработчик задается посредством атрибута
o n D b l C l i c k . Д а н н ы й обработчик не поддерживается Macintosh и Netscape 6.

onfocus()
Метод on f o c u s вызывается тогда, когда кнопка RESET получает фокус ввода. Для
определения данного метода используется атрибут on F o c u s .

25.29. Объект Screen


Объект S c r e e n , доступный посредством глобальной переменной s c r e e n , содер­
жит и н ф о р м а ц и ю о текущем р а з р е ш е н и и и цвете экрана.

Свойства
availHeight
Данное свойство, предназначенное только для чтения, содержит высоту экрана (в
пикселях) за вычетом области, занимаемой постоянными и н т е р ф е й с н ы м и эле­
ментами, такими как панель задач Windows 98.
2 5 . 3 0 . Объект Select 1227

availWidth
Данное свойство, предназначенное только для чтения, содержит ширину экрана
(в пикселях) за вычетом области, занимаемой постоянными интерфейсными эле­
ментами.

colorDepth
Свойство c o l o r D e p t h содержит число цветов, которые могут отображаться одно­
временно. Д а н н о е свойство доступно только для чтения.

height
Свойство h e i g h t , доступное только для чтения, содержит высоту экрана в пикселях.

width
Свойство w i d t h , доступное только для чтения, содержит ширину экрана в пикселях.

pixelDepth
Данное свойство определяет число битов, используемых для отображения цвета
одного пикселя. Свойство p i x e l D e p t h предназначено только для чтения.

Методы
Методы отсутствуют.

Обработчики событий
Обработчики событий отсутствуют. Объект S c r e e n не соответствует ни одному из
HTML-элементов.

25.30. Объект Select


Объект S e l e c t соответствует HTML-элементу, создаваемому с помощью дескрип­
тора <SELECT . . . >. Для доступа к объекту S e l e c t используется массив e l e m e n t s
объекта Ро2лп, содержащего элемент. Если ф о р м е и элементу присвоены имена, об­
ращение может производиться посредством выражения d o c u m e n t . formName.
s e l e c t N a m e , где formName — имя ф о р м ы , а s e l e c t N a m e — имя элемента SELECT.
В листинге 25.4 приведен код документа, к о т о р ы й содержит раскрывающееся ме­
ню, предназначенное для выбора цвета. Выбор пункта меню приводит к изменению
цвета ф о н а Web-страницы.
1228 Глава 25. Краткое руководство по JavaScript

Листинг 25.4.SelectColor.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Changing the Background Color</TITLE>
<SCRIPT TYPE="text/javascript">
<! —

function setBackgroundColor() {
var selection = document.colorForm.colorSelection;
document.bgColor =
selection.options[selection.selectedlndex].value;
}
// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="WHITE">
<Hl>Changing the Background Color</Hl>
<FORM NAME="colorForm">
<SELECT NAME="colorSelection"
onChange="setBackgroundColor ()">
<OPTION VALUE="#FFFFFF" SELECTED>White
<OPTION VALUE="#COCOCO">Gray
<OPTION VALUE="#FFOOOO">Red
.<OPTION VALUE="#OOFFOO">Green
<OPTION VALUE="#OOOOFF">Blue
</SELECT>

</FORM>

</BODY>
</HTML>

Свойства
form
Данное свойство ссылается на объект Form, содержащий элемент SELECT. Свойст­
во f o r m доступно только для чтения.

length
Свойство l e n g t h , предназначенное только для чтения, содержит данные о числе
элементов O p t i o n , находящихся в составе объекта SELECT. З н а ч е н и е l e n g t h сов­
падает со значением свойства o p t i o n s . l e n g t h .

name
Свойство name содержит имя, определяемое посредством атрибута NAME. Данное
свойство доступно только для чтения.
2 5 . 3 0 . Объект Select 1229

options
Свойство o p t i o n s предоставляет доступ к массиву объектов Option, содержащихся
в объекте S e l e c t . Новые объекты Option можно помещать в конец массива.

selectedlndex
Свойство s e l e c t e d l n d e x содержит индекс выбранного пункта. Если ни один из
пунктов не выбран, значение свойства равно -1. Если при создании элемента
SELECT был указан атрибут MULTIPLE, значением данного свойства является ин­
декс первого из выбранных пунктов. Свойство s e l e c t e d l n d e x допускает как чте­
ние, так и запись.

type
В зависимости от того, был ли указан при создании элемента атрибут MULTIPLE,
свойство t y p e содержит либо значение s e l e c t - o n e , либо значение s e l e c t -
m u l t i p l e . Это свойство доступно только для чтения.

Методы
blurO
В результате выполнения данного метода элемент SELECT теряет фокус ввода.

focus()
в результате выполнения метода focus элемент SELECT получает фокус ввода.

Обработчики событий

onblurO
Данный обработчик получает управление тогда, когда элемент теряет фокус ввода.
Обычно метод o n b l u r определяется посредством атрибута onBlur, как показано
ниже.
<SELECT . . . onBlur="doSomeAction()">

onchange()
Метод onchange получает управление, если элемент теряет фокус ввода после из­
менения его значения. Пример использования данного обработчика приведен в
листинге 25.4.

onfocus()
Метод onf OCUS вызывается тогда, когда элемент получает фокус ввода. Для опре­
деления данного обработчика используется атрибут onFocus.
1230 Глава 25. Краткое руководство по JavaScript

2 5 . 3 1 . Объект String
Объект S t r i n g чрезвычайно важен. Он не соответствует ни одному из HTML-
элементов, но очень часто используется в JavaScript-сценариях.

Конструктор
new String(value)
Данный конструктор создает новый объект S t r i n g .

Свойства
length
Данное свойство, предназначенное только для чтения, определяет число симво­
лов в строке.

Методы
anchor(name)
Метод anchor возвращает копию текущей строки, помещенную между дескрипто­
рами <А NAME="name"> и </А>. Например, выражение
"Chapter One".anchor("Chi")
эквивалентно следующему:
'<А NAME="Chl">Chapter One</A>'

bigO
Данный метод возвращает копию текущей строки, помещенную между дескрипто­
рами <BIG> и </BIG>.

blinkO
Данный метод возвращает копию текущей строки, помещенную между дескрипто­
рами <BLINK> и </BLINK>.

boldO
Метод b o l d возвращает копию текущей строки, помещенную между дескриптора­
ми <В> и </В>. Например, выражение
"Wow".italics () . b o l d O
эквивалентно следующему:
"<B><I>Wow</I></B>"

charAt(index)
Метод c h a r At возвращает строку, состоящую из одного символа, расположенного
в указанной позиции текущей строки. Подобно большинству типов данных в
JavaScript, отсчет символов начинается с нуля.
2 5 . 3 1 . Объект String 1231

charCodeAt()
charCodeAt(index)
Вызов первого метода эквивалентен вызову charCodeAt (О). Второй метод воз­
вращает код ISO Latin-1 символа, находящегося в указанной позиции строки. Пер­
вые 127 значений соответствуют ASCII-кодам.

concat(suffixString)
Данный метод предназначен для конкатенации двух строк. Следующие два выра­
жения эквивалентны:
var newString = stringl.concat(string2);
var newString = stringl + string2;

escape(stri]ig)
Ha самом деле e s c a p e не метод S t r i n g , a функция верхнего уровня. Однако, по­
скольку e s c a p e используется при работе со строками, ее описание приведено
здесь. Эта функция выполняет URL-кодирование строки символов. В результате
строка приобретает формат, в котором она может быть передана на сервер в со­
ставе URL. Заметьте, что в данном случае пробел заменяется не символом "+", а
последовательностью "%20". На рис. 25.5 показан результат выполнения приве­
денного ниже выражения.
alert(escape("Hello, world!"));

Рис. 25.5. Метод escape используется для выполнения


и RL-кодирования

URL-декодирование осуществляется с помощью метода u n e s c a p e .

fixedO
Данный метод возвращает копию строки, помещенную между дескрипторами
<ТТ>и</ТТ>.

fontcolor(colorName)
Метод f o n t c o l o r возвращает копию строки, помещенную между дескрипторами
<FONT COLOR="colorName"> и </FONT>.

fontsize(size)
Данный метод возвращает копию строки, помещенную между дескрипторами
<FONT SIZE=/?^^^>H</FONT>.
1232 Глава 25. Краткое руководство по JavaScript

fromCharCode(codeO, c o d e l , . . . , c o d e N )
Д а н н ы й метод создает строку, содержащую заданные символы ISO Latin-1. Этот
метод скорее можно отнести к конструкторам объекта S t r i n g . О н вызывается с
помощью выражения S t r i n g . f r o m C h a r C o d e ( . . . ) . Н а п р и м е р , приведенный
ниже код присваивает переменной h e l l o S t r i n g значение HELLO.
var helloString =
String.fromCharCode(72, 69, 76, 76, 79);

indexOf(substring)
indexOf(substring, startlndex)
Если заданная подстрока содержится в составе строки, первый из приведенных
методов возвращает индекс первого символа подстроки в строке. Если подстрока в
строке отсутствует, возвращается значение -1. Например, следующая функция воз­
вращает t r u e в случае, если вторая строка содержится в первой:
function contains(string, possibleSubstring) {
return(string.substring(possibleSubstring) != - 1 ) ;
}
Второй метод отличается от первого тем, что поиск указанной подстроки начина­
ется с заданного индекса. В результате выполнения метода возвращается индекс
первого вхождения относительно начала строки, а не относительно позиции
startlndex.

italics()
Данный метод возвращает копию строки, помещенную между дескрипторами <1>
и</1>.

lastIndexOf(substring)
lastIndexOf(substring, startlndex)
Если заданная строка входит в состав текущей строки, первый из указанных мето­
дов возвращает индекс начала последнего вхождения, в противном случае возвра­
щается значение -1. Второй метод отличается от первого тем, что поиск вхожде­
ния строки начинается с индекса, заданного с помощью второго параметра.

link(url)
Данный метод возвращает копию строки, помещенную между дескрипторами <А
HREF="url">H</A>.

match(regExp)
Данный метод возвращает массив, элементами которого являются фрагменты те­
кущей строки, соответствующие регулярному выражению, указанному в качестве
параметра m a t c h . Н а п р и м е р , в результате выполнения следующего кода будет
сформирован массив, содержащий строки "аЬс", " a b b b b c " , "ABC" и "АВВВВС".
v a r s t r = "abcabbbbcABCABBBBC";
var re = /ab+c/gi;
var r e s u l t = s t r . m a t c h ( r e ) ;
2 5 . 3 1 . Объект String 1233

Поскольку символ "g" в r e означает поиск по всей строке, а " i " — поиск без учета
регистра, регулярное выражение интерпретируется таким образом: "найти все
вхождения подстроки, состоящей из символа 'а' или 'А', за которым следует один
или более символов Ъ ' или *В', а затем расположен символ 'с' или ' С ". Подробно
о регулярных выражениях см, в разделе 25.27.

replace(regExp5 replacementString)
Метод r e p l a c e возвращает новую строку, сформированную путем замены всех
фрагментов, соответствующих регулярному выражению, указанной строкой. Если
при создании регулярного выражения был задан символ "д", то замене подлежат
все последовательности, соответствующие этому выражению. Например, в резуль­
тате выполнения приведенного ниже фрагмента кода будет сгенерирована строка
"We will use Java, Java, and Java".
var str = "We will use C, C++, and Java.";
var re = /C\+*/g;
var r e s u l t = s t r . r e p l a c e ( r e , "Java");

search(regExp)
Д а н н ы й метод возвращает значение t r u e или f a l s e , в зависимости от того, со­
держится ли в текущей строке подстрока, соответствующая регулярному выраже­
нию г е д Е х р . Если вас интересует только сам факт соответствия регулярному вы­
ражению, метод s e a r c h предпочтительнее метода m a t c h .

slice(startlndex, endlndex)
Если второй параметр представляет собой положительное число, метод s l i c e ра­
ботает т о ч н о так же, как и метод s u b s t r i n g . Однако в качестве второго парамет­
ра вы можете задать отрицательное число. Такое значение будет интерпретиро­
ваться как смещение от конца строки. Н и ж е приведено несколько примеров ис­
пользования метода s l i c e .
var s t r = "0123456789";
var str2 = str.sliced, 5 ) ; // "1234"
var str3 = str.substring(1, 5 ) ; // "1234"
var str4 = str.sliced, - 2 ) ; // "1234567"

small()
Данный метод возвращает копию строки, помещенную между дескрипторами
<SMALL> и </SMALL>.

splitO
Метод s p l i t возвращает массив, содержащий текущую строку. В таком виде метод
вряд ли пригоден. Ч т о б ы метод s p l i t выполнял полезные действия, надо при его
вызове указывать символ-ограничитель.
1234 Глава 25. Краткое руководство по JavaScript

split(delimChar)
Данный метод возвращает массив, сформированный путем разбиения текущей
строки на подстроки. Признаком окончания очередной подстроки является сим­
вол-разделитель delimChar в составе текущей строки. Например, в результате
выполнения следующего выражения будет сформирован массив, содержащий
строки "f со", "bar" и "baz" (в указанном порядке):
var t e s t = " f o o , b a r , b a z " . s p l i t ( " , " ) ;
Если в качестве символа-разделителя указан пробел, то подстроки могут разде­
ляться любым числом пробелов, символов табуляции и перевода строки. Метод
s p l i t выполняет действия, обратные методу j o i n объекта Array.

split(regExp)
В этом варианте метода s p l i t используется подстрока-разделитель, заданная с
помощью регулярного выражения. Например, при выполнении приведенного
ниже фрагмента кода будет создан массив из трех элементов, содержащий строки
"f со", "bar" и "baz" (в указанном порядке).
var s t r = " f o o , b a r , , , , , , b a z " ;
var re = / , + / ;
var r e s u l t = s t r . s p l i t ( r e ) ;

split(separator, limit)
Данный метод создает массив, число элементов которого не превышает значения
l i m i t . В качестве первого параметра может быть задан символ-разделитель либо
объект RegExp.

strike()
Данный метод возвращает копию строки, помещенную между дескрипторами
<STRIKE> и </STRIKE>.

sub()
Данный метод возвращает копию строки, помещенную между дескрипторами
<SUB>H</SUB>.

substr(startlndex, numChars)
Метод s u b s t г возвращает подстроку текущей строки длиной numChars, начи­
нающуюся с индекса s t a r t Index.

substring(startlndex, endlndex)
Данный метод возвращает новую строку, в которую входят символы текущей стро­
ки, начиная с индекса s t a r t Index и заканчивая индексом e n d l n d e x (символ, на
который указывает s t a r t Index, принадлежит, а символ, на который указывает
endlndex, — не принадлежит подстроке). В следующем примере переменной t e s t
присваивается строка " i s " :
var t e s t = " t h i s i s a t e s t " . s u b s t r i n g ( 5 , 7);
2 5 . 3 2 . Объект Submit 1235

supO
Данный метод возвращает копию строки, помещенную между дескрипторами <SUP>
H</SUP>.

toLowerCase()
Метод t o L o w e r C a s e возвращает копию текущей строки, символы которой преоб­
разованы в нижний регистр.

toUpperCase()
Метод t o U p p e r C a s e возвращает копию текущей строки, символы которой преоб­
разованы в верхний регистр.

unescape(string)
В действительности u n e s c a p e — не метод объекта S t r i n g , а функция верхнего
уровня. Однако, поскольку u n e s c a p e используется для работы со строками, мы
рассматриваем ее здесь. Функция u n e s c a p e выполняет URL-декодирование стро­
ки. Данная функция имеет недостаток: она не преобразует символ "+" в пробел.

Обработчики событий
Обработчики событий отсутствуют. Объект S t r i n g не соответствует ни одному из
HTML-элементов.

25.32. Объект Submit


Объект S u b m i t соответствует кнопке SUBMIT ф о р м ы , создаваемой с помощью де­
скриптора <INPUT TYPE="SUBMIT" . . . > . Для доступа к объекту S u b m i t использу­
ется массив e l e m e n t s соответствующего объекта Form. Если и кнопке SUBMIT, и
ф о р м е присвоены имена, обращаться к объекту S u b m i t можно посредством выраже­
ния d o c u m e n t . formName. s u b m i t B u t t o n N a m e , где formName— имя ф о р м ы , а
s u b m i t B u t t o n N a m e -- имя элемента.

Свойства
form
Данное свойство, предназначенное только для чтения, обеспечивает доступ к объ­
екту Form, содержащему кнопку SUBMIT.

name
Если п р и создании кнопки SUBMIT был задан атрибут NAME, значение атрибута
присваивается данному свойству. Свойство name доступно только для чтения.

type
Данное свойство имеет значение s u b m i t . Поскольку свойство t y p e принадлежит
всем объектам E l e m e n t , OHQ может использоваться для идентификации типа эле­
мента. Свойство t y p e предназначено только для чтения.
1236 Глава 25. Краткое руководство по JavaScript

value
Значение свойства v a l u e определяет надпись на кнопке. Д а н н о е свойство допус­
кает как чтение, так и запись.

Методы
blurO
В результате выполнения данного метода кнопка SUBMIT т е р я е т фокус ввода.

clickO
П р и вызове данного метода выполняются те же действия, что и после щелчка на
кнопке SUBMIT, но событие o n C l i c k не генерируется. Вместо метода c l i c k мож­
но вызывать метод s u b m i t ф о р м ы .

focus()
в результате выполнения данного метода кнопка SUBMIT получает фокус ввода.

Обработчики событий
onblur()
Данный метод вызывается тогда, когда кнопка SUBMIT т е р я е т фокус ввода. Обра­
ботчик o n b l u r определяется посредством атрибута o n B l u r , как показано ниже.
<INPUT TYPE="SUBMIT" ... onBlur="doSomeAction()">

onclick()
Метод o n c l i c k вызывается тогда, когда пользователь щелкает мышью на кнопке
SUBMIT. П р и вызове метода c l i c k метод o n c l i c k управления не получает. Дан­
ный обработчик определяется с помощью атрибута o n C l i c k .
<INPUT TYPE="SUBMIT" ... onClick="doSomeAction()">
Если метод o n c l i c k возвращает значение f a l s e , данные ф о р м ы не передаются
программе на стороне сервера. Например:
<INPUT TYPE="SUBMIT" . . . onClick="return(maybeSubmit())">
Аналогичный результат можно получить с помощью обработчика o n S u b m i t фор­
мы, содержащей кнопку.

ondblclick()
Данный метод вызывается после двойного щелчка мышью на кнопке. После пер­
вого щелчка вызывается метод o n c l i c k . Обработчик o n d b l c l i c k задается по­
средством атрибута o n D b l C l i c k . Этот обработчик не поддерживается Macintosh и
Netscape 6.
25.33. Объект Text 1237

onfocus()
Данный обработчик вызывается тогда, когда кнопка SUBMIT получает фокус ввода.
Он определяется посредством атрибута on F o c u s .

25.33. Объект Text


Объект T e x t соответствует полю редактирования в составе ф о р м ы , создаваемой
посредством дескриптора <INPUT TYPE="TEXT" . . . >. Для доступа к объекту T e x t
используется массив e l e m e n t s объекта Form, содержащего поле редактирования. Ес­
ли и полю, и ф о р м е присвоены имена, обращаться к элементу можно с помощью вы­
ражения d o c u m e n t . formName. t e x t f i e l d N a m e , где formName— имя ф о р м ы , а
t e x t f i e l d N a m e — имя элемента.

Свойства
defaultValue
Свойство d e f a u l t V a l u e содержит исходное содержимое поля редактирования,
заданное посредством атриб)п^а VALUE.

form
Данное свойство, предназначенное только для чтения, обеспечивает доступ к объ­
екту Form, содержащем)' поле редактирования.

name
Значение данного свойства соответствует значению атрибута NAME элемента. Это
свойство доступно только для чтения.

type
Свойство t y p e , предназначенное только для чтения, содержит значение t e x t .

value
Значение данного свойства соответствует тексту, содержащемуся в поле редакти­
рования. Свойство v a l u e допускает как чтение, так и запись.

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

focus()
в результате выполнения метода f o c u s поле редактирования получает фок)'с ввода.

selectO
Д а н н ы й метод выделяет текст в поле редактирования. Любой символ, введенный
пользователем, замещает выделенный текст.
1238 Глава 25. Краткое руководство по JavaScript

Обработчики событий
onblurO
Данный метод получает управление тогда, когда поле редактирования теряет фо­
кус ввода. О н определяется посредством атрибу1^а o n B l u r .

onchange()
Для того чтобы метод o n c h a n g e получил управление, надо чтобы поле редактиро­
вания потеряло фокус ввода после изменения его содержимого. Д а н н ы й обработ­
чик определяется посредством атрибута o n C h a n g e .

onfocus()
Данный обработчик вызывается тогда, когда поле редактирования получает фокус
ввода. Метод o n f e c u s определяется с помощью атрибута o n F o c u s .

onkeydownO
Этот метод вызывается после первого нажатия клавиши в тот момент, когда поле
редактирования имеет фокус ввода. Если o n k e y d o w n возвращает значение f a l s e ,
символ не вводится. Наличие обработчика o n k e y d o w n позволяет контролировать
вводимый текст.

onkeypress()
П р и первом нажатии клавиши пользователем обработчик o n k e y p r e s s вызывает­
ся после o n k e y d o w n . П р и последующих нажатиях клавиш o n k e y p r e s s вызывает­
ся, а o n k e y d o w n — нет. Если обработчик o n k e y p r e s s возвращает значение f a l s e ,
символ не вводится.

onkeyupO
Обработчик o n k e y u p вызывается тогда, когда пользователь отпускает клавишу.

25.34. Объект Textarea


Объект T e x t a r e a соответствует текстовой области— HTML-элементу, создавае­
мому посредством дескрипторов <TEXTAREA . . .> и </TEXTAREA>. Для доступа к
объекту T e x t a r e a используется массив e l e m e n t s объекта Form, содержащего тек­
стовую область. Если и текстовой области, и ф о р м е присвоены имена, обращаться к
элементу можно с помощью выражения d o c u m e n t . formName. t e x t a r e a N a m e , где
formName — имя ф о р м ы , а t e x t a r e a N a m e — имя текстовой области.

Свойства
defaultValue
Свойство d e f a u l t V a l u e соответствует начальному значению текстовой области,
т.е. содержит текст, находящийся между дескрипторами <TEXTAREA> и
</TEXT ARE А>. Это свойство доступно только для чтения.
25.34. Объект Textarea 1239

form
Данное свойство, предназначенное только для чтения, обеспечивает доступ к объ­
екту Form, содержащему текстовую область.

name
Свойство name соответствует значению атрибута NAME. Это свойство доступно
только для чтения.

type
Свойство t y p e предназначено только для чтения и содержит значение t e x t a r e a .

value
Значением свойства v a l u e является текст, содержащийся в данный момент в со­
ставе элемента. Это свойство допускает как чтение, так и запись, но поскольку оп­
ределить число строк и столбцов рассматриваемого элемента невозможно, помес­
тить в текстовую область корректно сформированный текст достаточно сложно.

Методы
blurO
В результате выполнения данного метода текстовая область теряет фокус ввода.

focus()
в результате выполнения метода focus текстовая область получает фокус ввода.

select()
Данный метод выделяет текст в области. Любой символ, введенный пользовате­
лем, замещает выделенный текст.

Обработчики событий
onblur()
Данный метод получает управление тогда, когда текстовая область теряет фокус
ввода. Он определяется посредством атрибута onBlur.

onchange()
Метод onchange получает управление при потере текстовой областью фокуса
ввода после изменения ее содержимого. Данный обработчик определяется по­
средством атрибута onChange.

onfocus()
Данный обработчик вызывается тогда, когда текстовая область получает фокус
ввода. Метод on focus определяется с помощью атрибута on Focus.
1240 Глава 25. Краткое руководство по JavaScript

onkeydown()
Этот метод вызывается после первого нажатия клавиши в тот момент, когда тексто­
вая область имеет фокус ввода. Если onkeydown возвращает значение f a l s e , символ
не вводится. Обработчик onkeydown позволяет контролировать вводимый текст.

onkeypress()
П р и первом нажатии клавиши пользователем обработчик on k e y p r e s s вызывает­
ся после o n k e y d o w n . П р и последующих нажатиях клавиш o n k e y p r e s s вызывает­
ся, а o n k e y d o w n не вызывается. Если обработчик o n k e y p r e s s возвращает значе­
ние f a l s e , символ не вводится.

onkeyupO
Обработчик o n k e y u p вызывается тогда, когда пользователь отпускает клавишу.

25.35. Объект Window


Объект Window описывает окно броузера или ф р е й м . Текущее окно доступно по­
средством переменной window, но обращаясь к свойствам и методам окна, вы можете
не указывать ее. Н а п р и м е р , для обращения к документу, связанному с текущим окном,
можно использовать как вырг1жение w i n d o w , d o c u m e n t , так и d o c u m e n t . Для того
чтобы отобразить в текущем окне другой документ, надо присвоить новое значение
свойству w i n d o w , l o c a t i o n . Как и в предыдущем случае, для обращения к этому
свойству достаточно задать имя l o c a t i o n .

Свойства
closed
Данное свойство содержит логическое значение, определяющее, было ли закрыто
окно. Свойство c l o s e d доступно только для чтения.

defaultStatus
Свойство d e f a u l t S t a t u s определяет строку, которая должна по умолчанию ото­
бражаться в строке состояния. Это свойство допускает как чтение, так и запись.

document
Данное свойство ссылается на объект D o c u m e n t , содержащийся в окне. Подробно
об объекте Document см. в разделе 25.5. Свойство d o c u m e n t предназначено толь­
ко для чтения.

frames
Свойство f r a m e s обеспечивает доступ к массиву, состоящему из элементов
Window. Каждый из элементов массива соответствует фрейм)' текущего документа.
25.35. Объект Window 1241

history
Данное свойство содержит объект H i s t o r y , связанный с текущим окном. Свойст­
во h i s t o r y доступно только для чтения.

innerHeight
Свойство i n n e r H e i g h t соответствует вертикальной составляющей внутреннего
размера окна. Это свойство допускает как чтение, так и запись. Изменение значе­
ния свойства приводит к изменению размеров окна.

innerWidth
Свойство innerWidth соответствует горизонтальной составляющей внутреннего
размера окна. Это свойство допускает как чтение, так и запись. Изменение значе­
ния свойства приводит к изменению размеров окна. Пример использования дан­
ного свойства см. в разделе 24.4.

Java
Данное свойство ссылается на объект Java Package, соответствующий верхнему
уровню иерархии пакетов j ava . *. Используя это свойство, вы можете, например,
обращаться к генератору псевдослучайных чисел посредством выражения
J a v a . l a n g . M a t h , random() или выводить данные в окно Java Console с помо­
щью выражения Java . l a n g . System, o u t . p r i n t l n . Свойство Java предназначе­
но только для чтения.

length
Данное свойство доступно только для чтения и означает то же, что и frames . l e n g t h .

location
Свойство l o c a t i o n ссылается на объект L o c a t i o n , содержащий URL, указанный
в составе запроса. Вследствие перенаправления он может отличаться от реального
URL, который является значением свойства document. URL. Свойство l o c a t i o n
допускает как чтение, так и запись. Изменение его значения приводит к отобра­
жению нового документа.

locationbar
В броузере Netscape подписанные сценарии могут устанавливать свойство
v i s i b l e l o c a t i o n b a r , что позволяет скрыть или отобразить поле L o c a t i o n .
Допустимыми значениями являются t r u e (либо 1) и f a l s e (либо 0).

Math
Данное свойство ссылается на объект Math.

menubar
В броузере Netscape подписанные сценарии могут устанавливать свойство
v i s i b l e menubar, что позволяет скрыть или отобразить полосу меню. Допусти­
мыми значениями являются t r u e (либо 1) и f a l s e (либо 0).
1242 Глава 25. Краткое руководство по JavaScript

name
П р и создании окна вы можете задать его имя. Имя окна является значением свой­
ства name. Это свойство предназначено как для чтения, так и для записи.

navigator
Данное свойство, предназначенное как для чтения, так и для записи, содержит
ссылку на объект N a v i g a t o r .

netscape
Свойство n e t s c a p e содержит ссылку на объект J a v a P a c k a g e , соответствующих
пакету n e t s c a p e . *. Д а н н о е свойство допускает как чтение, так и запись.

opener
Если текущее окно было создано из другого окна (родительского), свойство
o p e n e r содержит ссылку на родительское окно. Д а н н о е свойство допускает как
чтение, так и запись.

outerHeight
Свойство o u t e r H e i g h t соответствует вертикальной составляющей внешнего
размера окна. Это свойство допускает как чтение, так и запись. Изменение значе­
ния свойства приводит к изменению размеров окна. Окна размером менее 100x100
пикселей могут создаваться только защищенными (подписанными) сценариями.

outerWidth
Свойство o u t e r W i d t h соответствует горизонтальной составляющей внешнего
размера окна. Это свойство допускает как чтение, так и запись.

Packages
Данное свойство содержит ссылку на объект J a v a P a c k a g e , представляющий верх­
ний уровень иерархии пакетов. Свойство P a c k a g e s доступно только для чтения.

pageXOffset
Данное свойство содержит смещение по координате х страницы, соответствую­
щей области содержимого окна. Это свойство оказывается удобным при организа­
ции прокрутки. Свойство p a g e X O f f s e t предназначено только для чтения, чтобы
изменить его значение, надо использовать s c r o l l T o или s c r o l l B y .

pageYOffset
Данное свойство содержит смещение по координате у страницы, соответствующей
области содержимого окна. Свойство p a g e Y O f f s e t предназначено только для чте­
ния, чтобы изменить его значение, надо использовать s c r o l l T o или s c r o l l B y .

parent
Данное свойство, предназначенное только для чтения, ссылается на родительское
окно или ф р е й м . Для окна верхнего уровня w i n свойство w i n . p a r e n t содержит
значение w i n .
25.35. Объект Window 1243

personalbar
В броузере Netscape подписанные сценарии могут устанавливать свойство
v i s i b l e p e r s o n a l b a r , что позволяет скрыть или отобразить персональную па­
нель инструментов (панель каталогов). Допустимыми значениями являются t r u e
(либо 1) и f a l s e (либо 0).

screen
Данное свойство на самом деле является не свойством Window, а глобальной пере­
менной. Однако эта переменная рассматривается здесь, поскольку связана с текущим
окном. Об объекте Screen, на который ссылается s c r e e n , см. в разделе 25.29.

scrollbars
Подписанные сценарии могут устанавливать свойство v i s i b l e s c r o l l b a r s , что
позволяет скрыть или отобразить полосы прокрутки. Допустимыми значениями
являются t r u e (либо 1) и f a l s e (либо 0).

self
Данное свойство содержит ссылку на текущее окно и является синонимом window.
Свойство s e l f доступно только для чтения.

status
Значением данного свойства является строка, представляющая содержимое стро­
ки состояния. Свойство s t a t u s допускает как чтение, так и .запись. В первых
JavaScript-сценариях данное свойство использовалось для организации бегущей
строки, но, как выяснилось впоследствии, бегущая строка скорее мешает, чем спо­
собствует работе пользователя.

statusbar
Подписанные сценарии могут устанавливать свойство v i s i b l e s t a t u s b a r , что
позволяет скрыть или отобразить строку состояния. Допустимыми значениями
являются t r u e (либо 1) и f a l s e (либо 0).

sun
Данное свойство содержит ссылку на объект JavaPackage, соответствующий
верхнему уровню иерархии пакетов s u n . *. Свойство sun предназначено только
для чтения.

tags
Свойство t a g s может использоваться для установки свойств листов стилей.

toolbar
Подписанные сценарии могут устанавливать свойство v i s i b l e t o o l b a r , что по­
зволяет скрыть или отобразить панель инструментов Netscape. Допустимыми зна­
чениями являются t r u e (либо 1) и f a l s e (либо 0).
1244 Глава 25. Краткое руководство по JavaScript

top
Данное свойство ссылается на окно верхнего уровня, в состав которого входит те­
кущее окно. Если ф р е й м ы не используются, текущее окно является окном верхнего
уровня. Свойство t o p доступно только для чтения.

window
Свойство window, предназначенное только для чтения, ссылается на текущее окно
и является синонимом свойства s e l f .

Методы
alert(message)
Метод a l e r t отображает диалоговое окно с сообщением.

ЬаскО
Данный метод вызывает переход к предыдущему пункту списка предыстории, т.е.
производит те же действия, которые выполняются после щелчка мышью на кноп­
ке Back панели инструментов.

blurO
Метод b l u r приводит к потере фокуса ввода текущим окном; обычно при этом ок­
но переводится на задний план.

captureEvents(eventType)
Данный метод переводит окно в режим захвата событий указанного типа.

clear Interval(intervallD)
Метод s e t I n t e r v a l возвращает идентификатор. Передавая этот идентификатор
( i n t e r v a l l D ) методу c l e a r I n t e r v a l , можно завершить выполняющуюся процедуру.

clear T i m e o u t ( t i m e o u t l D )
Метод s e t T i m e o u t возвращает идентификатор. Передавая этот и д е н т и ф и к а т о р
( t i m e o u t I D ) методу c l e a r T i m e o u t , можно отменить выполнение запланирован­
ной процедуры.

close()
Данный метод закрывает окно. Вряд ли вы надеетесь, что вам будет разрешено за­
крывать окна, которые вы не создавали, однако из-за ошибок в реализации языка
вы можете сделать это.

confirm(questionString)
Данный метод выводит диалоговое окно, отображающее строку с сообщением, за­
данную в качестве параметра. Если пользователь щелкнет на кнопке ОК, возвраща­
ется значение t r u e . П р и активизации кнопки C a n c e l возвращается значение
25.35. Объект Window 1245

f a l s e . Если вы включите в состав сообщения символы перевода строки ' \ п ' , со­
общение будет отображаться в нескольких строках.

enableExternalCapture()
disableExternalCapture()
Подписанные сценарии имеют возможность перехватывать сообщения во внеш­
них страницах. Указанные два метода соответственно разрешают и запрещают де­
лать это.

findO
£ind(searchString)
find(searchString, caseSensitivityFlag, backwardFlag)
Метод f i n d выполняет поиск строки в текущем документе. Если метод вызван без
параметров, отображается диалоговое окно Find, посредством которого пользова­
тель может ввести строку для поиска. Помимо строки, при вызове метода можно за­
дать два логических значения. Второй параметр указывает, должен ли при поиске
учитываться регистр символов (если параметр имеет значение t r u e , осуществляется
поиск с учетом регистра), а третий параметр задает направление поиска (значение
t r u e предполагает поиск в обратном направлении). Если строка найдена, метод воз­
вращает значение t r u e , в противном случае возвращается значение f a l s e .

focus()
в результате выполнения метода f o c u s окно получает фокус ввода. На большин­
стве платформ при получении фокуса ввода окно перемещается на передний план.

forwardO
Выполнение данного метода вызывает переход вперед по списку предыстории.

handleEvent(event)
Если свойство c a p t u r e E v e n t s установлено, события указанного типа передаются
для обработки методу h a n d l e E v e n t .

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

m o v e B y ( x , у)
Метод moveBy вызывает перемещение окна на указанное число пикселей. В
Netscape перемещение окна за пределы экрана разрешено выполнять только под­
писанным сценариям, но даже в этом случае вызов данного метода может привес­
ти к нежелательным последствиям.
1246 Глава 2 5 . К р а т к о е руководство по JavaScript

m o v e T o ( x , у)
Метод moveBy размещает окно в указанной позиции. В Netscape размещение окна
за пределами экрана разрешено выполнять только подписанным сценариям, но
даже в этом случае вызов данного метода может привести к нежелательным по­
следствиям.

open(url, name)
o p e n ( u r l , n a m e , features)
o p e n ( u r l , n a m e , features, replaceFlag)
Д а н н ы й метод может быть использован для поиска существующего окна или для
открытия нового. Для того чтобы лучше различать методы w i n d o w . o p e n и
d o c u m e n t . o p e n , при вызове данного метода свойство window п р и н я т о указывать
явно. Если вместо URL задана пустая строка, открывается пустое окно. В него
можно записать данные, пользуясь свойством d o c u m e n t окна. Параметр name мо­
жет быть использован другими методами JavaScript либо указан в качестве значе­
ния атрибута TARGET элементов А, BASE, AREA и FORM. Параметр r e p l a c e F l a g
указывает, должен ли быть замещен имеющийся пункт списка предыстории ( t r u e )
либо создан новый ( f a l s e ) . Строка f e a t u r e s содержит список характеристик в
виде характеристикам значение (пробелы недопустимы!) и задает характеристики
окна броузера. Если параметр f e a t u r e s не задан, все характеристики считаются
разрешенными. Характеристика без указания значения эквивалентна выражению
характеристика^уез. Допустимые имена характеристик и их описания приведены
в табл. 25.2. В листинге 25.5 содержатся п р и м е р ы использования метода
w i n d o w , o p e n ; результаты показаны на рис. 25.6-25.9. Кроме того, см. п р и м е р соз­
дания окна был в разделе 24.10.

Внимсние!

Список характеристик не должен включать пробелов. Если это тре­


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

Таблица 2 5 , 2 . Обозначения хараюгеристик, используемые при вызове


м е т о д а open

Характеристика Допустимые Описание


значения
alwaysLowered yes/no Должно ли окно всегда находиться "ниже"
других. Доступна только для подписанных
сценариев
alwaysRaised yes/no Д о л ж н о ли окно всегда находиться поверх
других. Доступна только для подписанных
сценариев
dependent yes/no Является ли окно дочерним. Должно ли оно
при закрытии родительского окна закры­
ваться и удаляться из панели задач
2 5 . 3 5 . Объект Window 1247

Окончание табл. 25.2

directories yes/no Отображение кнопок на панели каталогов

hotkeys yes/no Запрет "горячих клавиш"

innerHeight число пикселей Высота области содержимого. В системе


Unix значение .Xdefaults отменяет данную
установку

innerWidth число пикселей Ш и р и н а области содержимого. В системе


Unix значение .Xdefaults отменяет данную
установку

location yes/no Должно ли отображаться поле Location

menubar yes/no Должна ли отображаться строка меню

OuterHeight число пикселей Вертикальная составляющая внешних раз­


меров окна

outerWidth число пикселей Горизонтальная составляющая внешних


размеров окна

Resizable yes/no Может ли пользователь изменять размеры


окна

ScreenX число пикселей Положение левой границы окна

ScreenY число пикселей Положение верхней границы окна

Scrollbars yes/no Могут ли использоваться полосы прокрутки

status yes/no Должна ли отображаться строка состояния


в нижней части окна

yes/no Должна ли отображаться строка заголовка.


titlebar
Запретить отображение строки заголовка
может только подписанный сценарий

yes/no Должна ли отображаться панель инстру­


toolbar
ментов, содержащая кнопки Back Forward
Home Stop и другие элементы

yes/no Запрет перемещения окна поверх или ниже


z-lock
других окон. Разрешено только для подпи­
санных сценариев
1248 Глава 25. Краткое руководство по JavaScript

print()
В результате выполнения данного метода документ выводится на печать, т.е. про­
изводятся те же действия, что и после щелчка на кнопке Print. Заметьте, что вызов
метода приводит к отображению диалогового окна, поэтому автор JavaScript-
сценария не может вывести данные на п р и н т е р без согласия пользователя.

prompt(message)

p r o m p t ( m e s s a g e , defaultText)
Д а н н ы й метод выводит диалоговое окно с полем редактирования. После закрытия
окна метод возвращает значение, введенное в поле. Параметр d e f a u l t T e x t по­
зволяет инициализировать поле редактирования заданным значением.

releaseEvents(eventType)
Данный метод отменяет режим перехвата событий указанного типа.

resizeBy(x, у)
Метод r e s i z e B y позволяет изменять размеры окна броузера на указанную вели­
чину.

r e s i z e T o ( x , у)
Метод r e s i z e T o дает возможность задавать размеры окна броузера.

routeEvent(event)
Д а н н ы й метод используется в методе h a n d l e E v e n t для передачи события по на­
значению.

scrollBy(x, у)
Данный метод выполняет прокрутку на )тсазанное число пикселей.

s c r o l l T o ( x , у)
Д а н н ы й метод выполняет прокрутку так, что указанная точка документа размеща­
ется в левом верхнем углу окна.

s e t I n t e r v a l ( c o d e , delay)
Данный метод постоянно выполняет заданный код до тех пор, пока окно не будет
закрыто либо пока не будет вызван метод c l e a r l n t e r v a l .

s e t T i m e o u t ( c o d e , delay)
Методу s e t T i m e o u t передается строка, содержащая JavaScript-код и время задержки
в миллисекундах. По истечении заданного интервала код выполняется. Чтобы отме­
нить выполнение кода, надо до окончания интервала вызвать метод c l e a r T i m e o u t ,
передав ему идентификатор, возвращенный методом s e t T i m e o u t . Заметьте, что
ме'год s e t T i m e o u t лишь планирует выполнение кода и возвращает управление, не
дожидаясь окончания времени задержки.
25.35. Объект Window 1249

stopO
Метод s t o p останавливает загрузку текущего документа, т.е. выполняет те же дей­
ствия, которые производятся после щелчка на кнопке Stop.

Обработчики событий
onblurO
Данный метод вызывается при потере окном фокуса ввода. Обработчик o n b l u r ,
как показано ниже, задается посредством атрибута o n B l u r дескриптора <BODY>
или <FRAMESET>.
<BODY o n B l u r = " a l e r t ( ' W e w i l l m i s s you')">

</BLUR>
Часто данный обработчик применяется для того, чтобы остановить выполняю­
щуюся процедуру в тот момент, когда пользователь переходит к другому окну. Вы­
полнение процедуры затем возобновляется обработчиком o n f o c u s .

ondragdropO
В броузере Netscape данный метод выполняется при перетаскивании в окно бро­
узера файла или ярлыка и последующем отпускании кнопки мыши. Указанный ме­
тод устанавливается посредством атрибута o n D r a g D r o p .

onerrorO
Данный метод вызывается при возникновении ошибки. Данному обработчику не
соответствуют атрибуты, поэтому его надо задавать непосредственно.
function reportError() {
return(!confirm("An error occurred.\n" +
"Please report i t to\n" +
"gates@microsoft.com.\n\n" +
"See more d e t a i l s ? " ) ) ;
}

onerror = reportError;
Если обработчик возвращает значение t r u e , броузер не сообщает об ошибке. Так,
в приведенном выше примере, если пользователь щелкнет на кнопке ОК, он уви­
дит лишь стандартное сообщение об ошибке. Установив значение o n e r r o r , рав­
ное n u l l , можно полностью подавить вывод сообщения об ошибке.

onfocus()
Данный метод вызывается при получении окном фокуса ввода. Обработчик
o n f o c u s задается посредством атрибута o n F o c u s дескриптора <BODY> или
<FRAMESET>. П р и м е р установки обработчика приведен ниже.
<FRAMESET ROWS=...
onFocus="alert('Welcome back')">

</FRAMESET>
1250 Глава 25. Краткое руководство по JavaScript

onload()
Метод o n l o a d вызывается по окончании загрузки документа броузером. Для уста­
новки данного обработчика используется атрибут о n L o a d дескриптора <BODY>
или <FRAMESET>. Д а н н ы й обработчик позволяет убедиться, что документ полно­
стью загружен и функции, зависящие от различных частей документа, будут рабо­
тать корректно.

onmove()
Метод onmove вызывается при перемещении окна (либо по инициативе пользова­
теля, либо программными средствами). Д а н н ы й обработчик устанавливается по­
средством атрибута onMove.
<BODY o n M o v e = " a l e r t ( ' H e y , move me b a c k ! ' ) " ...>

</BODY>

onresize()
Метод o n r e s i z e вызывается тогда, когда в результате действий пользователя или
выполнения JavaScript-кода размеры окна изменяются. Для установки данного об­
работчика используется атрибут o n R e s i z e .

onunloadO
Метод onunload вызывается тогда, когда отображение страницы прекращается.
Для установки данного обработчика используется атрибут onUnload дескрипто­
ров <BODY> или <FRAMESET>.

Листинг 25.5. OpenWindows. htJttl

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>Opening Windows with JavaScript</TITLE>

<SCRIPT TYPE="text/javascript">

function openSmallWindow() {
window.open("http://home.netscape.com/",
"smallWindow",
"width=375,height=125");

function openMediumWindow() {
window.open("http://home.netscape.com/",
"mediumWindow",
"width=550,height=225," +
"menubar,scrollbars,status,toolbar");
}

function openBigWindow() {
window.open("http://home.netscape.com/",
25.35. Объект Window 1251

"bigWindow",
"width=850,height=450," +
"directories, location,meniibar, " -l-
"scrollbars,status,toolbar");
}

// — >
</SCRIPT>
</HEAD>

<BODY>
<Hl>Opening Windows with JavaScript</Hl>

<FORM>
<INPUT TYPE="BUTTON" VALUE="Open Small Window"
onClick="openSmallWindow()">
<INPUT TYPE="BUTTON" VALUE="Open Medium Window"
onClick="openMediumWindow()">
<INPUT TYPE="BUTTON" VALUE="Open Big Window"
onClick="openBigWindow()">
</FORM>
</BODY>
</HTML>

£iie gdk yieM jgo i;;oiiRurk!«to( Ueip

^^ -ii^ # 3 ^ ^ a ^:»rfe a;
Opening Windows with Java^icript

'ш^~^^^1^ШШЩ
Рис. 25.6. Так выглядит документ OpenWindows.html до акти­
визации одной из кнопок

Ш |ШШШШШВШ

Netscape £
^itturdoy, r-ebrudty 24, 2001
(№«v«w<t<»t< Vt^fivai Screen Klnas To>

Рис. 25.7. Вызов метода window.open с указанием только ширины и


высоты приводит к отображению окна, в котором отсутствуют панель
инструментов, меню, строка состояния и другие компоненты.
(Приведено с разрешения Netscape Comnnunications Corp.)
1252 Глава 25. Краткое руководство по JavaScript


Fte
Netscape.com - Netscape
Edit View 60 Communicatof Help
33
- d f.r ^. ,МУ1 :„J Ш
я

Satut<}ay. F e b r u a r v 24, 2 0 0 1

Screen Kings Tax Time


Kurt Russell and Kevin The tax deadline is fast
Costner lead a gang of approaching. Consult our
Elvis impersonators on a comprehensive tax guide
heist in '3000 Miles to for all the resources you
Oraceiand.' Also debuting need to file ort time.
this weekend is Brendan ^f?

сй^-^ф- Doctffnent: Done '--^^ J '"^

Рис. 25.8. Окно броузера, в котором присутствуют некоторые ком­


поненты. (Приведено с разрешения Netscape Communications Corp.)

ШСБШЭгЖПШ!
:' •.' 4 -'Л .^ si i 'S Ъ 1; SI
^ Bookmafks .^' NetsSe jhUp /'/home netscape com/ ' What's Related
^imtentMrnage ? WebMa* ^ i Contact ^ 1 Poopte g i Ye8owP«ges S i Download J ^ Chanrwfe ^ ReaiPlajjw

ШЕ£-':с^ ^^I^J.^,^^^^!^^^

Screen Kings TdX Time HOTDTAiS K M


KurtRusftll «nd K«vin The tax d*«dlin« is f t s t • M u s t - H a v e s ; J.Crew
Costner le«d 4 9«п9 of «pproichmq, Consult oui Spring 2 0 0 1 for M e n

heist in ' 3 0 0 0 Miles to for «II the resources you 2 0 0 1 Collection


(3r*cel«nd.' Also debuting need to file on nme.
this weekend is Brendan ri^t? -«pt. T«'< •:•. j d« Entertelflsiisnt G o u r m e t Body Paint
Frasier in 'Monkeybone.'
Гагеу^рП 'Friorids'?
NBC's sitcom flagship is
sinking - IS it t i m e t o
put us out of its m i s e r y '
1
Enter Zip Code or City
ra
.. r ••'ШЗ TOP PICJcS
4» \ f WV
m» Ri'ih.cs'U £.=<-^iir<_'j-'i-C3D.'-?Lan act' Tt^il Us W h d t Y u u
» T e e n Suspect ir> D a r t m o u t h Murders Has Neo-Na;i Ties
S^M. , Think
У Powell Meets Ry?sian Cognterpart
T a k e our survey
yf^s * Labradors. Retrievers Lead Most Popular Poo List
forecasting t h e year
ahead.

.iSQ.OOQ

business tBaasi d
(йГ' '^'^'^ Doc^nent Don« ..ШЙЩ.-..;::.^...;:
Рис. 25.9. Указав все характеристики, вы можете отобразить окно броузера в привычном
виде. (Приведено с разрешения Netscape Communications Corp.)
25.35. Объект Window 1253

25.36. Резюме
Итак, книга прочитана. П р и м и т е наши поздравления. Мы надеемся, что вы освои­
ли основы HTML, Java и JavaScript настолько, что сможете разработать Web-
приложение от начала и до конца. Теперь самое время вернуться к тем главам, кото­
рые вы лишь бегло просмотрели, и прочитать их внимательно. Если вы уже знаете
стандартные средства HTML, может быть, вас заинтересуют листы стилей или меха­
низм слоев. Владея основами Java, наверное, следует подумать о более глубоком изу­
чении механизма потоков, RMI или JDBC. Возможно, вы имеете опыт создания CGL
программ. В этом случае стоит разобраться в том, как работают сервлеты. Вы увиди­
те, что сервлеты позволяют добиться тех же результатов, затрачивая намного мень­
шие усилия. В некоторых случаях регулярные выражения упрощают работу над соз­
данием JavaScript-сценария. П о н я т н о , что вы не можете блестяще разобраться с абсо­
лютно всеми вопросами, но, независимо от того, какое направление вы выберете для
дальнейшего изучения, базовые знания у вас уже есть.
Перед тем как продолжать работу, советуем вам день-два отдохнуть. И не забудьте
сказать начальнику, что ему следует подумать о вашем повышении.
Ж е л а е м успехов!
1254 Предметный указатель

ПРЕДМЕТНЫЙ УКАЗАТЕЛЬ

Abstract Window Toolkit, 455; 184 GIF89A, 94;712


Active Seiner Pages, 903
ActiveX, 101; 104
Apache XalanJ, 1078; 1088 H
Apache Xerces, 1053; 1055 HTML, 37
appletviewer, 196 HTML-форма, 785; 1190
ASCII, 48 HTML-элемент, 41
ASP,903 НТТР-сервер, 39; 751
AVI, 94 НТТР-туннелирование, 997; 1007
AWT, 183; 359; 455 HyperText Markup Language, 35
HyperText Transfer Protocol, 39

Cascading Style Sheets, Level 1, 137


CGI-перменная, 850 IMAP4, 193
C o m m o n Object Request Broker Internet Explorer, 36
.Architecture, 756 ISO, 85
cookie, 846; 871; 880; 1140
CORBA, 193; 756
CSS1,CSS2, 137
JAR-файл, 246
D Java, 179
Java 2D, 359
Document Object Model, 1051; 1052 Java API, 196
Document Type Definition, 1051 Java Database Connectivity, 192; 719; 1017
DOM, 1051; 1052 Java Development Kit, 192
DTD, 1051 Java Foundation Classes, 359
Java Naming and Directoiy Interface, 193
E Java Plug-In, 332; 359; 926
Java Runtime Environment, 332
ECMA, 1101 JavaBeans, 192; 932
ECMAScript, 1101 JavaHelp, 595
Enterprise JavaBeans, 193 JavaMail, 193
European Computer Manufacturers JavaScript, 80; 904; 1101; 1173
Association, 1101 JavaServer Pages, 193; 901
Extensible Style sheet Language for JavaServer Web Development Kit, 822
Transformations, 1051 JAXP, 193; 1052; 1054
JDBC, 192; 719; 1017
Предметный указатель 1255

JDBC-драйвер, 1017; 1018


JDK, 192
JNDI, 193 SAX, 1051; 1064
JRE, 332 ServletExec, 821
JRun, 821 Simple API for XML, 1051
JScript, 1102 Simple Mail Transfer Protocol, 728
JSP,901 Simple Object Access Protocol, 1051
JSWDK, 822 SMTP, 728
JVM, 183 SOAP, 1051
Socket, 719
SQL-запрос, 1020
SSI, 904
LiteWebServer, 821 STP, 193
LWS, 821 Swing, 334; 455; 543

M
MDI, 575 Tomcat, 821
М1МЕ-ТИП, 50; 93; 1207 Transformation API for XML, 1079
Model view controller architecture, 978 TrAX, 1079
Multiple Document Interface, 575
и
N UI-представитель, 544; 546
Netscape, 37 Unicode, 48; 85; 255
Uniform Resource Locator, 39
URL, 39

PHP, 903
POP3, 193 V
VBScript, 903
VRML, 101

QuickTime, 94
w
World Wide Web Consortium, 37
R
Remote Method Invocation, 192; 719; 756
Resin, 821 X
RMI, 192; 719; 756 XML, 193; 1051
RMI-IIOP, 193 XPath, 1078
RMI-клиент, 757 XSL, 1077; 1078
RMI-сервер, 757 XSLT, 1051; 1078
RPC, 756 XSLT-конвертирование, 1079
RSA, 681
1256 Предметный указатель

Вложенные контейнеры, 445


Вложенные списки, 62
Z-приоритет, 551 Вложенный класс, 401
Z-расположение, 551 Внешние листы стилей, 140
Внеэкранный буфер, 545; 651
Внутреннее окно, 575
Внутренний класс, 192; 401
Абсолютные единицы, 166 Вращение, 386
Абстрактный Вспомогательные метки шкалы, 568
класс, 233 Встроенное изображение, 94
метод, 234 Встроенные правила стилей, 142
Автоматический разбор строки пара­ Встроенный ф р е й м , 130; 132
метров, 836 Выбор файлов, 598
Адрес ресурса, 91 Вывод Swing-компонентов на принтер, 649
Активизация формы, 794 в поток ошибок, 273
Анимационная последовательность, 708 в стандартный выходной поток, 272; 317
Анимационный GIF-файл, 94 графики, 692
Анимация, 708 изображения, 340
Анонимный класс, 192 Вызов методов Java из JavaScript, 1148
Аплет, 101; 194; 311 Выравнивание, 57; 59
Арифметическая операция, 258; 1204 Выражение, 904; 905; 1018
Архитектура модель-просмотр-конт­
роллер, 546; 603; 978
Атрибут, 41
А ф ф и н н ы е преобразования, 365 Генерация документации, 223
Гипертекстовая ссылка, 90; 1200
Гнездо, 719
сервера, 745
Базовая позиция, 46 Гонки,672
Библиотека, 187 Горизонтальная л и н и я , 79
дескрипторов, 946 Градиент, 359; 371
классов, 242 Градиентная линия, 371
Бинарное дерево, 297 Градиентное заполнение, 371
Блок, 159 Грамматические правила Java, 253
catch, 302 Графическое п р и л о ж е н и е , 334
finally, 302; 305 Группа потоков, 690
try, 302 Группировка интерфейсных элементов, 812
комментариев, 253
Буферизация, 919
Д
В Данные
запроса, 836
Ввод-вывод, 271 таблицы, 66
Вектор,295 ф о р м ы , 836
Взаимодействие JavaScript и Java, 1148 Дата, 1180
Визуализация ячеек таблицы, 643 Двойная буферизация, 545; 651; 704
Вирт)'альная машина Java, 183 Двухмерная таблица, 603; 634
Предметный указатель 1257

Действие, 902 Заголовок таблицы, 66


Декларация, 904; 910 Загрузка драйвера, 1018
HTML для работы с фреймами, 43 Закладка для фрейма, 125
Декодирование,848 Закрывающий дескриптор, 42
Дерево, 297 Заполнение
Д е с к р и п т о р , 36 контура изображениями, 373
javadoc, 224 ф о р м ы , 370
синхронизации,673 Запрос, 1018
Деструктор, 214 GET, 991
Децентрализованная обработка POST, 991; 1007
событий, 493 Заранее
Динамическая поддержка типов, 1110 о т ф о р м а т и р о в а н н ы й текст, 58
Динамически создаваемые древовидные подготовленные выражения, 1046
структуры, 629 скомпилированные выражения, 1046
Динамическое Зарезервированные имена фреймов, 124
изменение изображений, 1122
создание HTML-документа, 1103
Директива, 902; 913
И
Д и с п е т ч е р компоновки, 419 И д е н т и ф и к а т о р , 145; 254
BorderLayout, 422 сеанса, 892
BoxLayout, 439 Изображение, 1194
CardLayout, 427 Имя
FlowLayout, 420 класса, 209
GridBagLayout, 432 константы, 209
GridLayout, 425 переменной, 209
Доклет, 224 Инициализация сервлета, 830
Док}^мент, 1183 Java, 195
Дополнительный модуль, 104; 1217 Интеграция сервлетов и JSP, 977
броузера, 1120 И н т е р ф е й с , 235
Д о ч е р н и й класс, 230 к удаленному объекту, 757
Древовидная структура, 603; 621 прослушивания, 395
Дуга, 366 И н ф о р м а ц и я об авторе, 61
Дуговые градусы, 366 Исключение, 302
определяемое разработчиком, 306
Использование аплетов для выполнения
JavaScript-операций, 1148; 1150
Ж и з н е н н ы й цикл аплета, 318
сервлета, 830
К
Каркас, 757
Карта изображения на стороне,
Завершение клиента, 98; 806
потока, 688 сервера, 98; 806
экземпляра сервлета, 833 Каскадирование, 146
Заглушка, 757 Каскадные листы стилей, 36; 137
Заголовок, 56 Класс адаптера, 403
HTTP-запроса, 841
1258 Предметный указатель

Класс Метод, 210


поддержки дескриптора, 946 JavaScript, 1114; 1173
селекторов, 144 запроса, 842
ЬСлиент, 719 передачи данных, 786
Клиентское гнездо, 745 доступа, 219
Кнопка, 496; 794; 1177 Многомерный массив, 294
JavaScript, 798 Многопотоковая программа, 190; 666
RESET, 1225 Многопотоковый метод, 677
SUBMIT, 1235 Модальное диалоговое окно, 486
с зависимой фиксацией, 504 Модель, 546
Код состояния, 853 данных, 604
Кодировка, 786 определяемая разработчиком,
Комментарии, 42 612; 627
Компонент, 460 обработки событий, 192
Canvas, 456 Модификатор, 210; 247
ScrollPane, 474 Модификация URL, 892
Конечный дескриптор, 42
Конструктор
JavaScript, 1173
н
класса, 211 Набор результатов, 1020
Контейнер, 42; 472 Наследование, 230
Контекстное меню, 535 Начальный, дескриптор, 42
Контроллер, 546 Невизуальный броузер, 38
Контрольная точка, 367 Неименованный внутренний класс, 402
Контур формы, 362 Нелинейное масштабирование, 386; 389
Кривая, 367 Необрабатываемое исключение, 307
Безье, 367 Низкоуровневая обработчка событий, 409
Нумерованный список, 61; 165
Л
Легковесный компонент, 544
О
Лексема, 254; 726 Область, 366
Лексические правила Java, 253 видимости, 248
Линейное масштабирование, 363 Обновление содержимого окна, 316
Линейный регулятор, 530; 567 фрейма, 126
Листы стилей JavaScript, 142 Обработка событий JavaScript, 1108
Логический стиль, 88 Обработчик событий, 395
Локальная переменная, 209 Обрамление таблицы, 67; 68
Локальный шрифт, 359; 378 Обращение к JavaScript из Java, 1157
Обтекание текста, 163
Объединение пикселей, 364
М Объект, 186; 207
Маркированный список, 61; 64; 165 JavaScript, 1111
Массив, 291 блокировки, 673
JavaScript, 1116; 1173 прослушивания, 395
Масштабирование, 386 Объектно-ориентироавнный язык, 186
Меню, 480
Предметный указатель 1259

Объектно-ориентированное програм­ ф р е й м , 130; 132


мирование, 207 П о в т о р н о е использование классов, 218
Ограничитель, 726 Поддержка сеанса, 891; 918
Однопотоковая программа, 666 Подкласс, 230
Ожидание соединения, 746 Поиск информации о базе данных, 1019
Окно, 1240 Поле
Dialog, 486 ввода пароля, 792; 1215
FileDialog, 489 заголовка, 841; 853; 864
Frame, 475 класса, 208
Window, 491 редактирования, 518; 790; 1237
списка, 510; 802 Полностью определенное имя класса, 242
Округление, 258 Полоса прокрутки, 530
Описания определений, 65 Получение данных, 836
О п ц и и javadoc, 227 Пользовательский дескриптор, 946
Основной абзац, 58 Порт, 720
Открывающий дескриптор, 42 Порядок получения фокуса ввода, 813
Относительные единицы, 166 Поток, 665; 832
Относительный URL, 46 доставки событий, 658
Очередь событий, 660 Swing, 658
Правила стилей,138
п Предопределенные объекты, 912
переменные, 905; 912
Пакет, 242 Представитель пользовательского
Панель, 469 интерфейса, 544; 546
Панель инструментов, 583 Предшествование правил, 146
Перевод строки, 108 Преобразование
Передача JSP в сервлет, 902
параметров, 282; 311; 327 системы координат, 359; 386
аплету, 103 Преобразователь, 1079
по значению, 282 П р и л о ж е н и е , 194; 311
по ссылке, 282 П р и м и т и в ы , 255
содержимого ф о р м ы , 795 Проверка HTML-форм, 1131
строки параметров, 785 Программа на стороне клиента, 781
Переключатель опций, 504; 597; сервера, 781
799; 800; 1218 П р о з р а ч н о е рисование, 359
Переменная this, 212 Прозрачность, 370; 376
класса, 209 ф о р м ы , 364
окружения CLASSPATH, 245 Промежуточная декларация HTML, 43
экземпляра, 208 П р о с м о т р , 546
Перенаправление, 857 П р о с т ы е типы, 255
запросов, 978 Прямоугольник, 368
методов, 215 со скругленными углами, 368
П е р о , 359 Псевдокласс якоря, 146
П е ч а т ь фрейма, 126 Пул экземпляров сервлетов, 833
Пиктограммы на кнойках, 559 Пункт
Плавающая панель инструментов, 586 меню, 1214
плавающий элемент, 163 списка, 61; 64
1260 Предметный указатель

Пустой фрейм, 130 Сериализация, 192; 999


окон,482
Сжатие документов при передаче, 848
Синтаксис Java, 253
Работа с Синхронизация, 673
cookie средствами LavaScript, 1140 метода, 674
фреймами средствами JavaScript, 1144 посредством произвольного
Рабочий каталог, 40 объекта, 676
Разбиение окна броузера, 113 посредством объекта Class, 676
Разбор посредством разделяемых данных, 676
средствами DOM, 1053 фрагмента кода, 674
средствами SAX, 1064 Скриптлет, 904; 908
текста, 726 Скрытое поле, 811; 892
Развертывание, 757 Скрытый элемент ф о р м ы , 1192
Раздел, 79 Слой, 167; 1128; 1197
Разделитель, 254 Смешивание цветов, 391
Разметка, 35 Событие, 395
Размещение JavaScript, 1173
компонентов вручную, 443 древовидной структуры, 626
экрана, 1226 таблицы, 645
Рамка вокруг компонента, 564 таймера, 713
Раскрывающееся меню, 535; 1227 управляющего элемента, 492
Раскрывающийся список, 507; 802 Совместимость с броузером, 1107
Расширяемый массив, 295 Соединение с
Регистрация обработчика события, 396 FTP-сервером, 725
Рег)^лярное выражение, 1219 базой данных, 1017; 1019
Рекурсия, 297 Создание гнезда, 720
Рисование ф о р м ы , 365 Специальный символ, 1223
Родительский класс, 230 Спецификация
H T T P 1.1, 845
Java Servlet, 820
JavaServer Pages, 820
Сборка мусора, 179; 214 Список, 603; 604
Свойство, 138 определений, 61; 65; 165
JavaScript, 1173 предыстории,1193
класса, 208 Сплайн,367
Связывание Сравнение строк, 286
деревьев, 622 Ссылка, 186; 280
информации с сеансом, 895 Ссылочное значение, 280
потока с гнездом, 721 Стандартные обработчики событий, 403
Сглаживание, 364; 390 Статическая переменная, 209
Сдвиг, 386 Статический
Селектор,138; 143 инициализационный блок, 214
Сервер,719; 745 текст, 526
баз данных, 1019 Стиль, 83
промеж)точного уровня, 817 Java, 547
Сервлет, 193; 817 MacOS, 547
Предметный указатель 1261

Metal, 547
Motif, 547
Ф
Windows, 547 Файл описания, 246
о ф о р м л е н и я конца отрезка, 381 Файл описания библиотеки дескрип­
рисования, 364 торов, 946; 947
соединения линий, 381 Физический стиль, 83
Строгая декларация HTML, 43 Фильтрация данных, 839
Строка, 284; 1230 Флаг завершения, 688
комментариев, 253 Флажок опции, 501; 595; 799; 1178
параметров, 781; 836 Фокус, 93; 117
состояния,853 Форма, 78; 1190
таблицы, 66; 71 Формирование запроса аплетом, 991
Структура данных DOM, 1053 Фрейм, 37; 111; 1144; 1240
Суперкласс, 230 Функция JavaScript, 1110; 1191
Сценарий, 1101

Ц
Цвет пера, 381
Таблица, 66 Целочисленные типы, 256
Таймер, 712 Централизованная обработка
Текст шаблона, 904 событий, 495
Текстовая область, 523; 589; 792; 1238 Цикл, 258; 265
Текстовый блок, 55 Цитирование, 60; 85
Тело пользовательского дескриптора, 956 Ц и ф р о в а я подпись, 311
Т е р м и н ы определений, 65
Толщина
линии,359
ч,ш
пера, 381 Число, 1210
Тяжеловесный компонент, 544 с плаваюш,ей точкой, 256
Ч т е н и е из стандартного входно­
го потока, 274
Шаблон, 359; 381
Удаленный вызов процедур, 756
объект, 757
Указатель, 186; 280
Управление аплетами из JavaScript-сце- Экземпляр класса, 207
нариев, 1148; 1153 Элемент блокового уровня, 50; 55
Усечение, 257 массива, 291
Условный сценария, 902; 904
запрос, 857 текстового уровня, 50; 83
о п е р а т о р , 2 5 8 ; 259 ф о р м ы , 1186
Научно-популярное издание
Марти Холл, Лэрри Браун

Программирование для Web.


Библиотека профессионала

Л и т е р а т ) р н ы й редактор ИЛ. Попова


Верстка А.В. Плаксюк
Художественный редактор С.А. Чернокозинский
Технический редактор Г.Н. Горобец
Корректоры Л.А. Гордиенко, О.В. Мишутина

Издательский дом "Вильяме".


101509, Москва, ул. Лесная, д. 43, стр. 1.
Изд. лиц. ЛР № 090230 от 23.06.99
Госкомитета РФ по печати.

Подписано в печать 28.12.2001. Формат 70x100/16.


Гарнитура NewBaskerville. Печать офсетная.
Усл. печ. л. 112. Уч.-изд. л. 66,5.
Тираж 5000 экз. Заказ № 2515.

Отпечатано с диапозитивов в ФГУП "Печатный двор"


Министерства РФ по делам печати,
телерадиовещания и средств массовых коммуникаций.
197110, Санкт-Петербург, Чкаловский пр., 15.

Вам также может понравиться