Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Секреты программирования для Internet на Java

.pdf
Скачиваний:
181
Добавлен:
02.05.2014
Размер:
3.59 Mб
Скачать

<APPLET CODE="AnimatedCursorApplet.class" HEIGHT=250 WIDTH=250>

<PARAM NAME="CURSORFILE0" VALUE="http://www.vmedia.com/vvc/onlcomp/java/chapter5/images/anim0.gif"> <PARAM NAME="CURSORFILE1" VALUE="http://www.vmedia.com/vvc/onlcomp/java/chapter5/images/anim1.gif"> <PARAM NAME="CURSORFILE2" VALUE="http://www.vmedia.com/vvc/onlcomp/java/chapter5/images/anim2.gif"> <PARAM NAME="CURSORFILE3" VALUE="http://www.vmedia.com/vvc/onlcomp/java/chapter5/images/anim3.gif"> <PARAM NAME="CURSORFILE4" VALUE="http://www.vmedia.com/vvc/onlcomp/java/chapter5/images/anim4.gif"> </APPLET></BODY></HTML>

Устранение мерцания

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

Рис. 5.8.

Когда оболочка времени выполнения должна повторно перерисовывать графические окна - например, когда окно было затенено или когда апплет запросил, чтобы окно было повторно перерисовано, - вызывается метод модификации апплета и ему передается объект Graphics графического окна. Апплет может запрашивать перерисовку графики методом paint. Метод paint вызывает метод модификации как можно скорее. Если апплет запрашивает другую перерисовку прежде, чем метод модификации вызвался из первой, апплет модифицируется только в первый раз. По умолчанию методы модификации просто передают объект Graphics методу paint.

В апплете можно определить виртуальный экран, чтобы рисовать на нем, вместо того чтобы обращаться непосредственно к графическому окну оболочки времени выполнения. Нарисованное изображение апплет может перенести на реальное окно с помощью метода getCraphics. Этот метод возвращает объект Graphics, связанный с изображением. Апплет будет рисовать на нем с той скоростью, с какой сможет. Метод модификации апплета выведет изображение на объект Graphics, вместо того чтобы разрешить апплету вывести его прямо на экран. В результате мерцание должно исчезнуть.

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

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

Пример 5-6a. Анимированный апплет отображения курсора без мерцания. import java.awt.*;

import java.applet.*; import java.net.*;

public class AnimatedCursorApplet extends Applet implements Runnable {

Ⱦɚɧɧɚɹ ɜɟɪɫɢɹ ɤɧɢɝɢ ɜɵɩɭɳɟɧɚ ɷɥɟɤɬɪɨɧɧɵɦ ɢɡɞɚɬɟɥɶɫɬɜɨɦ %RRNV VKRS Ɋɚɫɩɪɨɫɬɪɚɧɟɧɢɟ ɩɪɨɞɚɠɚ ɩɟɪɟɡɚɩɢɫɶ ɞɚɧɧɨɣ ɤɧɢɝɢ ɢɥɢ ɟɟ ɱɚɫɬɟɣ ɁȺɉɊȿɓȿɇɕ Ɉ ɜɫɟɯ ɧɚɪɭɲɟɧɢɹɯ ɩɪɨɫɶɛɚ ɫɨɨɛɳɚɬɶ ɩɨ ɚɞɪɟɫɭ piracy@books-shop.com

private int mouse_x, mouse_y; private Image CursorImages[]; private int CursorIndex = 0; private Thread anim = null; private boolean paused = false; private Image OffscreenImage; // наш виртуальный экран

private Graphics OffscreenGraphics; // наш интерфейс к нему

public void init() { resize(250,250);

OffscreenImage = createImage(250,250); // новое изображение

OffscreenGraphics = OffscreenImage.getGraphics(); // привяжем наш интерфейс к изображению

anim = new Thread(this); mouse_x = 125;

mouse_y = 125;

CursorImages = new Image[10]; int i;

String CursorParam; URL CursorURL;

for (i=0; i<5; i++) {

CursorParam = getParameter("CURSORFILE"+i); try {

CursorURL = new URL(CursorParam); CursorImages[i] = getImage(CursorURL);

}

catch (MalformedURLException e) { CursorImages[i] = createImage(0,0);

}

}

}

public void start() { anim.start();

}

public void stop() {

if (anim!=null && anim.isAlive()) { anim.stop();

}

}

public synchronized void update(Graphics g) { paint(OffscreenGraphics);

//скажем апплету закрашивать наше изображение g.drawImage(OffscreenImage,0,0,this);

//закрасим реальное графическое окно

}

public void paint(Graphics g) { int px, py;

Image Cursor = CursorImages[CursorIndex]; g.setColor(Color.white); g.fillRect(0,0,249,249);

//рисуем по старому изображению g.setColor(Color.black); g.drawRect(0,0,249,249);

//выведем рамку

px = mouse_x - Cursor.getWidth(this)/2; py = mouse_y - Cursor.getHeight(this)/2; g.drawImage(Cursor,px,py,this);

}

public boolean mouseMove(Event evt, int x, int y) { mouse_x = x;

mouse_y = y; return true;

www.books-shop.com

}

public boolean mouseDown(Event evt, int x, int y) { if (paused) {

anim.resume(); paused = false;

}

else {

anim.suspend(); paused = true;

}

return true;

}

public void run() {

while (anim!=null) { try {

anim.sleep(50);

}

catch (InterruptedException e) {} CursorIndex = CursorIndex + 1; if (CursorIndex==5) {

CursorIndex = 0;

}

repaint();

}

}

}

Что дальше?

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

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

www.books-shop.com

Глава 6

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

Основы API Структура API

Использование API

Класс java.lang.Object

Работа со строками Создание строк Сравнение строк Работа с подстроками Изменение строк Разбор строк

Преобразование строк в другие типы данных Упаковщики примитивных типов

Классы-контейнеры Класс Vector

Хеш-таблицы Стеки Интерфейсы API

Особо важные интерфейсы Интерфейс Enumeration

Интерфейсы java.lang.Clonable и java.lang.Runnable

Обработка событий при помощи java.util.Observer Математика и API

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

API - это огромный набор инструментальных средств с широкими функциональными возможностями, которые приходят с Java Developers Kit. В нем вы найдете инструментальные средства манипулирования строками, работы с сетями, математические возможности, а также "рабочие лошадки" - программы работы с хеш-таблицами (не путайте их с "кэшем" - это совсем другое) и стеками. Все это богатство, обеспечиваемое API, - фундамент Java. Фактически, мы уже его использовали. Классы Applet, URL, Image, String и System - это все члены API. Помните оператор import, которым мы пользовались в прошлой главе? Каждый раз, когда мы ставим за ним нечто, начинающееся с "java", мы обращаемся к API.

www.books-shop.com

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

API можно использовать почти для любой создаваемой программы. Фактически, класс Object, который находится наверху иерархии классов Java, - это самостоятельный член API. Большая часть вашей работы как программиста Java будет заключаться в изучении того, какие средства имеются в вашем распоряжении в наборе инструментальных средств API. В этой главе внимание сосредоточено на той части API, которая сохранит вас от огромного количества рутинной работы, когда вы начнете создавать все более сложные апплеты. Например, когда мы будем описывать интерфейс пользователя в главе 7, изображения в главе 9 и работу апплетов с сетями в части IV, в действительности мы будем пользоваться кодом, уже содержащимся в API.

Основы API

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

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

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

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

Структура API

API - это совокупность нескольких десятков готовых классов, интерфейсов и исключений. Внутри API эти классы, интерфейсы и исключения сгруппированы в восемь пакетов: java.applet, java.awt, java.awt.image, java.awt.peer, java.io, java.lang, java.net и java.util. В табл. 6-1 кратко описано назначение каждого пакета.

 

Таблица 6-1. Пакеты API

Пакет

Описание

java.applet Содержит классы и интерфейсы, которые запускают апплет (см. главу 5, "Апплет в работе").

java.awt Позволяет создавать графические интерфейсы пользователя.

Java.awt.peer Составлен полностью из интерфейсов, которые позволяют системе работать с окнами Java. Легко переносимы на другие платформы (но не представляют никакого интереса для среднего программиста).

java.awt.image Предназначен для создания и манипулирования изображениями (см. главу 9, "Графика и изображения").

java.io Обрабатывает ввод и вывод программы (см. главу 11, "Многопотоковость").

java.lang Содержит основные элементы java.language, такие как Object, String, Excerption и Thread.

www.books-shop.com

Java.net Обрабатывает взаимодействие с сетью (см. главы 11, "Многопотоковость", и 12, "Программирование за рамками модели апплета").

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

Не забывайте, что пакет - это связанный набор классов и интерфейсов, которые позволяют обращаться к методам и переменным, определенным с модификатором protected. Во многих случаях члены входят в какой-то из пакетов, что объясняется соображениями дизайна, связанными с использованием ключевого слова protected. В других случаях какой-то из членов может принадлежать к некоторому пакету из-за того, что по своей цели он ближе всего к другим членам именно этого пакета. Используя API, мы можем думать о различных пакетах как о семействах функциональных возможностей. Не обманитесь на счет java.awt.image и java.awt.peer - их члены не имеют никакой специальной синтаксической связи с членами пакета java.awt. Создатели Java выбрали для API такие имена, чтобы показать, что члены двух пакетов являются подобными по функциональным возможностям.

СОВЕТ Подклассы других классов API не обязательно находятся в том же самом пакете, что и их родитель. Например, класс Applet существует в java.applet, но его суперкласс находится в java.awt.

При ежедневном программировании структура пакета важна для вас в двух случаях. Вопервых, вам нужно использовать оператор import, чтобы обратиться к классу в API. А во-вторых, вы должны быть хотя бы в общем знакомы с содержанием пакетов, потому что интерактивная документация Java Developers Kit, как показано на рис. 6-1, также организована в виде пакетов. Каждый класс, интерфейс и исключение имеют собственную Web-страницу с подробным описанием всех доступных общих методов и переменных.

Рис. 6.1.

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

www.books-shop.com

Использование API

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

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

Импорт пакетов

Уже в главе 2 мы начали импортировать пакеты и описали синтаксис полностью в главе 3. Как вы помните, для этого достаточно написать:

import <название_пакета>. *;

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

class MyApplet extends java.applet.Applet {

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

СОВЕТ Пакет java.lang настолько необходим, что компилятор всегда импортирует его.

Рассмотрим рецепт наилучшего использования оператора import на примере класса java.util.Date, позволяющего получать текущее время и дату.

1.Определите пакет, содержащий класс, который вы хотите использовать. Это довольно просто: так как полное имя нашего класса - java.util.Date, следовательно, он входит в пакет java.util. В противном случае мы проконсультировались бы с интерактивной документацией по JDK, чтобы найти соответствующий пакет.

2.Импортируйте класс оператором import. Если мы используем только класс Date, мы могли бы сделать так:

import java.util.Date;

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

import java.util.*;

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

import Java.util;

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

Date d;

или иначе: java.util.Date d;

С оператором import java.util.* или без него последний код правилен. Однако такая форма объявления неуклюжа, и в ней легко ошибиться.

Статические методы против динамических

Как вы помните из главы 2, методы Java могут быть или статическими, или динамическими. Динамические методы намного более общие - они связаны с конкретизацией понятий класса. С другой стороны, статические методы являются методами, которые связаны с классом непосредственно. Метод является статическим, если в его объявлении присутствует модификатор static. На рис. 6-2 показан статический метод с именем parse в классе java.util.Date. Обратите внимание на присутствие ключевого слова static.

www.books-shop.com

Рис. 6.2.

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

public long demoStatlc(String S) { return Date.parse(S);}

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

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

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

С другой стороны, статические переменные и методы являются только утилитами, которые принадлежат некоему классу, потому что этот класс - то место, где вы вполне логично искали бы их. Давайте рассмотрим, к примеру, метод parse из класса Date. Этот метод берет строку, которая форматируется как дата, и возвращает вам число секунд, прошедших с полуночи 1 января 1970 года по Гринвичу.

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

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

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

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

Обращаться к методу нужно с синтаксисом <имя класса>.<имя метода>.

Обзор других модификаторов доступа

www.books-shop.com

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

Модификатор protected. Означает, что метод или переменная доступны только другим классам этого пакета. Так как мы создаем классы, которые не будут входить ни в какой из пакетов API, этот модификатор пока не накладывает на нас никаких ограничений. Мы доберемся до использования ключевого слова protected при проектировании и выполнении собственных пакетов в главе 11, "Многопотоковость".

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

Модификатор abstract. Если весь класс определен с модификатором abstract, мы можем реализовывать только его подклассы. Если только один из членов класса определен с модификатором abstract, мы можем обращаться только к тому члену подкласса, который заменяет этот специфический метод (см. главу 3, "Объектная ориентация в Java"). Когда вы видите модификатор abstract, не забудьте, что дело обстоит не так, как кажется на первый взгляд. Вы должны, вероятно, рассмотреть дерево API, которое включено в документацию, и исследовать подклассы, обеспечиваемые API. На рис. 6-3 показано дерево абстрактного класса java.awt.Component. Понимание подклассов этого класса - большая часть изучения Java AWT, и мы исследуем их полностью в следующих двух главах.

Рис. 6.3.

Модификаторы synchronized и native. Не имеют никакого реального значения для пользователей API. Чтобы дать о них общее представление, скажем, что модификатор sinchronized означает, что к специфическому методу или переменной можно обратиться только в определенное время, что важно для многопотоковых программ (см. главу 11). Но теперь нам не нужно волноваться относительно этого модификатора.

Модификатор native определяет, что метод фактически выполнен в динамической связанной библиотеке (DLL), которая не была написана на Java. Предполагается, что все методы, определенные с модификатором native, вставляются в код, написанный на Java.

www.books-shop.com

Но пока нам не нужно обращать особое внимание в том случае, если метод, который мы хотели бы использовать, определен с модификатором native. (Более подробно мы рассмотрим такие методы в главе 12, "Программирование за рамками модели апплета".)

Исключения и API

Изучая интерактивную документацию по Java, вы можете обратить внимание на то, что некоторые методы и конструкторы вызывают исключения. Мы столкнулись с конструктором, вызывающим исключение, в главе 5, "Апплет в работе", - конструктор для класса URL, который содержится в пакете java.net. На рис. 6-4 приведена Web-страница класса URL, показывающая, что конструктор, который получает строку, вызывает исключение MalformedURLException.

Рис. 6.4.

Чтобы эффективно использовать API, вы должны знать исключения, которые может вызывать метод или конструктор. Написание собственных подпрограмм, обрабатывающих исключения, будет рассмотрено в главе 10, "Структура программы". Сейчас же мы только представим вам рецепт для имеющихся методов API и конструкторов, вызывающих исключения. Основная стратегия показана на диаграмме потока на рис. 6-5.

www.books-shop.com