Метод getFile
Метод getFile позволяет получить информацию о файле, связанном с данным объектом URL:
public String getFile();
Метод getHost
С помощью метода getHost вы можете определить имя узла, соответствующего данному объекту URL:
public String getHost();
Метод getParameterInfo
Метод getParameterInfo возвращает описание единственного параметра нашего аплета, через который передается имя звукового файла.
Метод getPort
Метод getPort предназначен для определения номера порта, на котором выполняется связь для объекта URL:
public int getPort();
Метод getProtocol
С помощью метода getProtocol вы можете определить протокол, с использованием которого установлено соединение с ресурсом, заданным объектом URL:
public String getProtocol();
Метод GetRecordByNumber
Метод GetRecordByNumber позволяет извлечь произвольную запись из файла данных по ее порядковому номеру.
Напомним, что смещения всех записей хранятся в файле индексов и имеют одинаковую длину 8 байт. Пользуясь этим, метод GetRecordByNumber вычисляет смещение в файле индекса простым умножением порядкового номера записи на длину переменной типа long, то есть на 8 байт, а затем выполняет позиционирование:
idx.seek(nRec * 8);
После этого метод GetRecordByNumber извлекает из файла индексов смещение нужной записи в файле данных, вызывая для этого метод readLong, а затем выполняет позиционирование в файле данных:
idxFilePointer = idx.readLong();
dat.seek(idxFilePointer);
Поля записи читаются из файла данных в два приема. Вначале читается строка текстового поля, а затем - численное значение, для чего вызываются, соответственно, методы readLine и readInt:
str = dat.readLine();
account = new Integer(dat.readInt());
Полученные значения полей объединяются в текстовой строке и записываются в переменную sRecord:
sRecord = new String("> " + account + ", " + str);
Содержимое этой переменной метод GetRecordByNumber возвращает в качестве извлеченной строки записи базы данных.
Метод getRef
Метод getRef возвращает текстовую строку ссылки на ресурс, соответствующий данному объекту URL:
public String getRef();
Метод hashCode
Метод hashCode возвращает хэш-код объекта URL:
public int hashCode();
Метод imageUpdate
Метод imageUpdate периодически вызывается в процессе загрузки изображения, конструируя каждый раз значение флага полной загрузки fAllLoaded следующим образом:
fAllLoaded = ((flags & ALLBITS) != 0);
Когд изображение будет полностью загружено, в параметре flags метода imageUpdate будет установлен флаг ALLBITS, после чего флаг fAllLoaded будет установлен в значение true.
Как только это произойдет, метод imageUpdate вызовет метод repaint, выполнив принудительную перерисовку окна аплета:
if(fAllLoaded)
repaint();
При этом метод paint нарисует в окне аплета изображение фона, закрасив им сообщение о ходе процесса загрузки изображения.
Метод imageUpdate должен возвратить значение false или true. Если изображение еще не загружено, возвращается значение true:
return !fAllLoaded;
При этом метод imageUpdate будет вызываться еще раз для отслеживания процесса загрузки.
Когда загрузка будет завершена, метод imageUpdate возвратит значение false, после чего этот метод вызываться больше не будет.
Метод init
Метод init вызывается один раз при инициализации аплета. Наше приложение его не использует.
Метод init
Единственная задача метода init нашего приложения - получение параметров аплета и запись их в соотвестствующие поля класса. Эта задача решается с помощью метода getParameter, при этом строка param типа String используется как рабочая:
String param;
param = getParameter(PARAM_String1);
if (param != null)
m_String1 = param;
Аналогичным образом метод получает значения всех шести параметров.
Метод init
Метод init получает текущие значения параметров аплета и сохраняет их в соответствующих полях основного класса. Способ получения параметров аналогичен использованному в предыдущем приложении.
Метод init
В начале своей работы метод init записывает в поле sOut текстовую строку, которая будет записана в выходной поток:
String sOut;
sOut = "Hello, Java!";
Далее метод init создает поток baStream класса ByteArrayOutputStream, устанавливая начальный размер выделенного для него массива равным 255 байт:
ByteArrayOutputStream baStream =
new ByteArrayOutputStream(255);
Для выполнения форматированного вывода нам нужен поток класса DataOutputStream, который мы и создаем на базе потока baStream:
OutStream = new DataOutputStream(
new BufferedOutputStream(baStream));
Для записи строки в выходной поток мы воспользовались методом writeBytes:
OutStream.writeBytes(sOut);
Так как наш выходной поток буферизован, после вызова метода writeBytes данные могут остаться в промежуточном буфере, не достигнув массива, выделенного для хранения потока. Чтобы переписать данные из буфера в массив, мы выполняем сброс буфера методом flush:
OutStream.flush();
После сброса буфера (и только после этого) можно копировать содержимое потока методом toByteArray:
bMemStream = baStream.toByteArray();
Этот метод возвращает ссылку на созданный массив, которую мы записываем в поле bMemStream. В дальнейшем на базе этого массива мы создадим поток ввода.
Перед завершением своей работы метод init закрывает входной поток,вызывая метод close:
OutStream.close();
Метод init
Во время инициализации метод init создает объект класса URL для файла исходных данных:
SrcURL = new URL("http://frolov/chart.txt");
Здесь для экономии места в книге мы указали адрес URL файла исходных данных непосредственно в программе, однако вы можете передать этот адрес аплету через параметр в документе HTML.
Далее для нашего объекта URL мы создаем канал и получаем содержимое объекта (то есть исходные данные для построения диаграммы):
URLContent = SrcURL.openConnection().getContent();
Здесь использована двухступенчатая процедура получения содержимого с созданием канала как объекта класса URLConnection. Вы также можете упростить этот код, воспользовавшись методом getContent из класса URL:
URLContent = SrcURL.getContent();
Результат в обоих случаях будет одинаковый - содержимое файла исходных данных окажется записанным в поле URLContent класса Object.
Если при создании объекта класса URL возникло исключение, метод init записывает в поле errno код ошибки, равный 2, записывая при этом в строку состояния навигатора сообщение “MalformedURLException exception”.
В том случае, когда объект класса URL создан успешно, а исключение возникло в процессе получения содержимого файла, в поле errno записывается значение 1, а в строку состояния навигатора - сообщение "getContent exception".
Метод init
Метод init создает два объекта класса Image для файлов disk.gif и cd.gif:
FloppyDiskImg = getImage(getCodeBase(), "disk.gif");
CDDiskImg = getImage(getCodeBase(), "cd.gif");
В качестве первого параметра методу getImage передается адрес URL аплета, полученный при помощи метода getCodeBase. При этом предполагается, что файлы disk.gif и cd.gif находятся в том же каталоге, что и аплет.
Метод init
Прежде всего метод init создает объект класса MediaTracker, который будет использоваться для отслеживания процесса загрузки изображений:
mt = new MediaTracker(this);
Далее метод init последовательно создает три объекта класса Image (соответственно, для изображений фона, для флоппи-диска и для компакт-диска), а затем добавляет их в объект MediaTracker с помощью метода addImage:
BkgImg = getImage(getCodeBase(), "bkg.gif");
mt.addImage(BkgImg , 0);
FloppyDiskImg = getImage(getCodeBase(), "disk.gif");
mt.addImage(FloppyDiskImg, 0);
CDDiskImg = getImage(getCodeBase(), "cd.gif");
mt.addImage(CDDiskImg, 0);
Метод init
В процессе инициализации аплета метод init создает объект класса Image, соответствующий изображению фона:
public void init()
{
BkgImg = getImage(getCodeBase(), "bkg.gif");
}
Как вы уже знаете, при этом реальная загрузка файла изображения не выполняется.
Метод init
Сразу после запуска аплета метод init получает значение параметра - имя звукового файла, и если этот параметр задан в документе HTML, записывает полученное имя в поле m_ClipName:
param = getParameter(PARAM_ClipName);
if(param != null)
m_ClipName = param;
Далее создаются три кнопки, управляющие звучанием аплета:
btPlay = new Button("Play");
btLoop = new Button("Loop");
btStop = new Button("Stop");
Кнопка Stop блокируется, так как на данный момент проигрывание еще не запущено:
btStop.disable();
Для блокирования вызывается метод disable, определенный в классе Button.
Подготовленные таким образом кнопки добавляются в окно аплета:
add(btPlay);
add(btLoop);
add(btStop);
Напомним, что работа с кнопками и другими органами управления в приложениях Java была нами описана в 30 томе “Библиотеки системного программиста”, который называется “Microsoft Visual J++. Создание приложений на языке Java. Часть 1”.
Последнее, что делает метод init перед тем как возвратить управление, это получение ссылки на интерфейс AudioClip:
auClip = Applet.getAudioClip(getCodeBase(),m_ClipName);
Адрес URL каталога, в котором расположен аплет, определяется с помощью метода getCodeBase, о котором мы говорили в предыдущей главе.
Метод init
Метод init создает три кнопки, предназначенные для управления аплетом Audio, причем кнопка с названием Stop блокируется:
btPlay = new Button("Play");
btLoop = new Button("Loop");
btStop = new Button("Stop");
btStop.disable();
Далее созданные кнопки добавляются в окно аплета Inspector.
add(btPlay);
add(btLoop);
add(btStop);
Метод init
Метод init вызывается независимо от режима, в котором работает приложение - автономно или как аплет.
После изменения размеров окна аплета методом resize метод init определяет некоторые параметры среды выполнения приложения. Для этого используется метод getProperty, определенный в классе System. Подробное описание этого метода выходит за рамки нашей книги. Однако мы отметим, что передавая этому методу в виде текстовой строки названия параметров среды выполнения, можно получить в текстовом виде значения этих параметров.
Наш метод init использует метод getProperty для определения названия операционной системы, под управлением которой работает приложение, передавая ему строку "os.name":
String str = System.getProperty("os.name");
Далее метод init проверяет, есть ли в строке названия операционной системы слово Windows, и если есть, пытается запустить программу калькулятора:
if(str.indexOf("Windows") != -1)
{
Runtime rt = Runtime.getRuntime();
try
{
rt.exec("calc.exe");
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
Калькулятор запускается с помощью класса Runtime, который предоставляет системно-зависимые методы (именно поэтому перед его использованием мы проверили название операционной системы).
Ссылка на объект класса Runtime должна быть получена с помощью статического метода getRuntime. Получив такую ссылку, мы запускаем программу калькулятора с помощью метода exec, который определен в реализации класса Runtime для операционных систем Microsoft Windows 95 и Microsoft Windows NT.
Заметим, что одно из важнейших достоинств приложений Java заключается в возможности достижения независимости от платформ, на которых они выполняются. Поэтому использование класса Runtime оправдано только тех случаях, когда приложение Java разрабатывается специально для конкретной платформы.
Метод main
Сразу после запуска метод main приложения DirectFileAccess создает базу данных, передавая конструктору имена файла индекса dbtest.idx и файла данных dbtest.dat:
SimpleDBMS db = new SimpleDBMS("dbtest.idx", "dbtest.dat");
После этого с помощью метода AddRecord, определенного в классе SimpleDBMS, в базу добавляются три записи, состоящие из текстового и числового полей:
db.AddRecord("Ivanov", 1000);
db.AddRecord("Petrov", 2000);
db.AddRecord("Sidoroff", 3000);
Сразу после добавления записей приложение извлекает три записи с номерами 2, 1 и 0, вызывая для этого метод GetRecordByNumber, также определенный в классе SimpleDBMS:
System.out.println(db.GetRecordByNumber(2));
System.out.println(db.GetRecordByNumber(1));
System.out.println(db.GetRecordByNumber(0));
Извлеченные записи отображаются на системной консоли.
После завершения работы с базой данных она закрывается методом close из класса SimpleDBMS:
db.close();
Метод mouseEnter
В предыдущем томе “Библиотеки системного программиста” мы рассказывали о методах mouseEnter и mouseExit. Первый из этих методов вызывается, когда в результате перемещения курсор мыши оказывается над окном аплета, а второе - когда курсор покидает окно аплета. Мы переопределили эти методы в своем аплете.
Когда курсор мыши оказывается над окном аплета, мы временно приостанавливаем работу задачи, вызывая метод suspend:
public boolean mouseEnter(Event evt, int x, int y)
{
if (m_Rectangles != null)
{
m_Rectangles.suspend();
}
return true;
}
Преостановленная задача не уничтожается. Ее работа может быть продолжена с помощью метода resume.
Метод mouseExit
Когда курсор мыши покидает окно аплета, вызывается метод mouseExit. Этот метод в нашем аплете возобновляет работу задачи, временно приостановленной методом suspend. Для этого используется метод resume, как это показано ниже:
public boolean mouseExit(Event evt, int x, int y)
{
if (m_Rectangles != null)
{
m_Rectangles.resume();
}
return true;
}
Метод openConnection
Метод openConnection предназначен для создания канала между приложением и сетевым ресурсом, представленным объектом класса URL:
public URLConnection openConnection();
Если вы создаете приложение, которое позволяет читать из каталогов сервера Web текстовые или двоичные файлы, можно создать поток методом openStream или получить содержимое текстового ресурса методом getContent.
Однако есть и другая возможность. Вначале вы можете создать канал, как объект класса URLConnection, вызвав метод openConnection, а затем создать для этого канала входной поток, воспользовавшись методом getInputStream, определенным в классе URLConnection. Такая методика позволяет определить или установить перед созданием потока некоторые характеристики канала, например, задать кэширование.
Однако самая интересная возможность, которую предоставляет этот метод, заключается в организации взаимодействия приложения Java и сервера Web.
Подробнее методика организации такого взаимодействия и класс URLConnection будет рассмотрен позже.
Метод openStream
Метод openStream позволяет создать входной поток для чтения файла ресурса, связанного с созданным объектом класса URL:
public final InputStream openStream();
Для выполнения операции чтения из созданного таким образом потока вы можете использовать метод read, определенный в классе InputStream (любую из его разновидностей).
Данную пару методов (openStream из класса URL и read из класса InputStream) можно применить для решения задачи получения содержимого двоичного или текстового файла, хранящегося в одном из каталогов сервера Web. Сделав это, обычное приложение Java или аплет может выполнить локальную обработку полученного файла на компьютере удаленного пользователя.
Метод paint
Метод paint рисует в окне аплета текстовую строку и случайное число, полученное при помощи статического метода random класса Math:
public void paint(Graphics g)
{
g.drawString("Running: " + Math.random(), 10, 20);
}
Напомним, что в однозадачном приложении метод paint вызывается при первом создании окна аплета, а также в случае необходимости перерисовки этого окна.
В нашем аплете будет создана отдельная задача, выполняющая периодическую перерисовку окна при помощи метода repaint. Поэтому случайное число в окне аплета будет постоянно меняться.
Метод paint
В предыдущем приложении метод paint периодически получал управление в результате периодического вызова метода repaint, выполняемого отдельной задачей. Метод paint аплета Rectangles вызывается только при инициализации и тогда, когда нужно обновить окно аплета. Этот метод определяет текущие размеры окна аплета, закрашивает окно желтым цветом и рисует вокруг окна черную рамку.
Метод paint
Метод paint подготавливает окно аплета для рисования - закрашивает его в желтый цвет и рисует вокруг окна черную рамку.
Метод paint
Метод paint просто закрашивает окно аплета желтым цветом и затем обводит его черной рамкой.
Метод paint
После традиционного для наших аплетов раскрашивания окна и рисования рамки метод paint создает входной буферизованный поток на базе массива bMemStream:
InStream = new DataInputStream(
new BufferedInputStream(
new ByteArrayInputStream(bMemStream)));
Поток создается в три этапа с помощью классов ByteArrayInputStream, BufferedInputStream и DataInputStream.
Далее мы читаем из созданного таким образом входного потока одну строку, вызывая для этого метод readLine:
g.drawString(InStream.readLine(), 10, 20);
Прочитанная строка отображается в окне аплета методом drawString.
После завершения работы с потоком мы его закрываем, для чего вызываем метод close:
InStream.close();
Метод paint
После раскрашивания фона окна аплета и рисования вокруг него рамки метод paint приступает к рисованию круговой диаграммы.
Прежде всего метод проверяет, является ли полученный из сети объект текстовой строкой класса String. Если является, то выполняется явное преобразование типа:
if(URLContent instanceof String)
{
sChart = (String)URLContent;
}
В случае успеха в переменной sChart будет находиться строка исходных данных для построения диаграммы, а при ошибке - строка “<error>”, записанная туда при инициализации. Кроме того, в поле errno записывается значение 3.
Далее метод paint проверяет, были ли ошибки при создании объекта URL, получении содержимого файла исходных данных или преобразования данных в строку класса String. Если были, то в строку состояния навигатора записывается код ошибки и содержимое строки sChart. Если же ошибок не было, то в строке состояния отображаются исходные данные:
if(errno != 0)
showStatus("errno: " + errno + ", sChart: " + sChart);
else
showStatus(sChart);
На следующем этапе обработчик paint приступает к построению диаграммы.
Первым делом создается разборщик строки исходных данных:
StringTokenizer st =
new StringTokenizer(sChart, ",\r\n");
В качестве разделителей для этого разборщика указывается запятая, символ возврата каретки и перевода строки.
Рисование секторов диаграммы выполняется в цикле, условием выхода из которого является завершение разбора строки исходных данных:
while(st.hasMoreElements())
{
. . .
}
Для того чтобы секторы диаграммы не сливались, они должны иметь разный цвет. Цвет сектора можно было бы передавать вместе со значением угла через файл исходных данных, однако мы применили более простой способ раскаршивания секторов - в случайные цвета. Мы получаем случайные компоненты цвета сектора, а затем выбираем цвет в контекст отображения:
rColor = (int)(255 * Math.random());
gColor = (int)(255 * Math.random());
bColor = (int)(255 * Math.random());
g.setColor(new Color(rColor, gColor, bColor));
Метод paint
После раскрашивания окна аплета в белый цвет и рисования вокруг окна черной рамки метод paint вызывает четыре раза метод drawImage, рисуя изображения флоппи-диска и компакт-диска:
g.drawImage(FloppyDiskImg, 25, 42, 200, 200, this);
g.drawImage(FloppyDiskImg, 25, 3, this);
g.drawImage(CDDiskImg , 70, 3, this);
g.drawImage(CDDiskImg , 115, 3, 40, 25, this);
В первый раз флоппи-диск рисуется с масштабированием, во второй раз - в исходном виде. Компакт-диск вначале рисуется в исходном виде, а затем - растянутым по горизонтали.
Метод paint
Метод paint прежде всего раскрашивает окно аплета в белый цвет и обводит его черной рамкой. Затем на подготовленной таким образом поверхности он пишет сообщение о начале процесса загрузки изображений:
g.drawString("Подождите, идет загрузка...",
20, dimAppWndDimension.height / 2);
Далее ожидается загрузка всех изображений, для чего вызывается метод waitForAll из класса MediaTracker:
try
{
mt.waitForAll();
}
catch (InterruptedException ex)
{
}
Когда все изображения будут загружены, следует серия вызовов метода drawImage, с помощью которых рисуется изображение фона, два изображения флоппи-диска и два изображения компакт-диска:
g.drawImage(BkgImg, 1, 1,
dimAppWndDimension.width - 2,
dimAppWndDimension.height - 2, this);
g.drawImage(FloppyDiskImg, 25, 42, 200, 200, this);
g.drawImage(FloppyDiskImg, 25, 3, this);
g.drawImage(CDDiskImg , 70, 3, this);
g.drawImage(CDDiskImg , 115, 3, 40, 25, this);
Метод paint
Свою работу метод paint начинает с раскрашивания окна аплета и рисования рамки вокруг окна. Затем метод проверяет флаг fAllLoaded, начальное значение которого равно false:
if(fAllLoaded == false)
{
g.drawString("Подождите, идет загрузка...",
20, dimAppWndDimension.height / 2);
}
Флаг fAllLoaded служит индикатором полной загрузки изображения и устанавливается методом imageUpdate, отслеживающим загрузку. Пока значение этого флага равно false, метод paint отображает в окне аплета сообщение о том, что идет процесс загрузки.
Когда изображение будет полностью загружено, метод imageUpdate устанавливает значение флага fAllLoaded, равное true, а затем принудительно перерисовывает окно аплета, вызывая метод repaint. При этом метод paint рисует в окне аплета полностью загруженное изображение фона:
g.drawImage(BkgImg, 1, 1,
dimAppWndDimension.width - 2,
dimAppWndDimension.height - 2, this);
Метод paint
Сразу после получения управления, метод paint закрашивает окно аплета белым цветом и рисует вокруг него черную рамку.
Затем метод проверяет содержимое флага m_fAllLoaded. Этот флаг установлен в значение true, когда все кадры видеофильма загружены и сброшен в значение false, когда загрузка кадров еще не завершена. Последняя ситуация возникает всегда при первом вызове метода paint.
Если все изображения загружены, метод paint вызывает метод displayImage, определенный в нашем приложении:
if(m_fAllLoaded)
{
displayImage(g);
}
Этот метод, о котором мы еще расскажем подробнее, отображает в окне аплета текущий кадр видеофильма.
Если же кадры видеофильма еще не загружены, в окне аплета отображается соответствующее сообщение:
else
g.drawString("Подождите, идет загрузка...",
10, dimAppWndDimension.height / 2);
Метод paint
После раскрашивания фона окна и рисования рамки метод paint получает с помощью метода getAppletContext ссылку на интерфейс AppletContext:
appContext = getAppletContext();
Далее с помощью этой ссылки и метода getApplets приложение получает список всех аплетов, расположенных в текущем документе HTML;
eApplets = appContext.getApplets();
Вслед за этим метод paint запускает цикл, в котором он получает ссылки на все найденные аплеты:
while(eApplets.hasMoreElements())
{
. . .
}
В этом цикле с помощью метода nextElement приложение получает ссылку на очередной аплет и, после преобразования ее к типу Applet, сохраняет в переменной currentApplet:
Applet currentApplet = (Applet)(eApplets.nextElement());
Для каждого найденного аплета вызывается метод getAppletInfo:
appName = currentApplet.getAppletInfo();
Полученная строка обрезается до первого символа возврата каретки или перевода на новую строку и записывается в переменную appName:
StringTokenizer st;
st = new StringTokenizer(appName, "\r\n");
appName = new String((String)st.nextElement());
Содержимое этой переменной (имя аплета) отображается в окне аплета Inspector со сдвигом по вертикали, который завивит от номера найденного аплета:
g.drawString(appName , 10, 15 * i + 50);
В том случае, если в процессе получения строк информации об аплете был найден аплет Audio, выполняется преобразование типа ссылки на этот аплет и сохранение этой ссылки в поле appAudio:
if(appName.equals("Name: Audio"))
{
appAudio = (Audio)currentApplet;
}
Метод paint
После раскрашивания окна аплета метод paint анализирует содержимое поля m_fStandAlone, определяя режим работы приложения.
Если приложение работает автономно, метод отображает в окне приложения соответствующее сообщение:
g.drawString("Приложение работает автономно", 10, 20);
Затем метод paint определяет и отображает такие параметры среды выполнения приложения, как название архитектуры компьютера, название и версия операционной системы:
str =
"System: " + System.getProperty("os.arch") +
", OS: " + System.getProperty("os.name") +
", ver. " + System.getProperty("os.version");
g.drawString(str, 10, 50);
Строкой ниже отображается путь к каталогу, в котором находится двоичный модуль приложения и путь к каталогу, где находится библиотка классов Java:
str =
"User dir: " + System.getProperty("user.dir") +
", User home: " + System.getProperty("user.home");
g.drawString(str, 10, 65);
В том случае, когда приложение запущено как аплет, последние два параметра не определяются, так как они недоступны для аплетов из соображений безопасности.
Метод paint класса MultiTask2 не
Метод paint класса MultiTask2 не делает ничего нового по сравнению с аналогичным методом предыдущего аплета. Он просто раскрашивает окно аплета в желтый цвет и рисует вокруг него черную рамку.
Метод run
Метод run получает управление при запуске задачи методом start. Если этот метод возвращает управление, соответствующая задача завершает свою работу.
Наша реализация метода run состоит из бесконечного цикла, в котором периодически с задержкой 50 миллисекунд вызывается метод repaint:
public void run()
{
while(true)
{
try
{
repaint();
Thread.sleep(50);
}
catch(InterruptedException e)
{
stop();
}
}
}
Метод repaint вызывает принудительную перерисовку окна аплета, выполняемую методом paint. В нашем приложении этот метод отображает текстовую строку и случайное число.
Для выполнения задержки метод run вызывает метод sleep из класса Thread. Так как метод sleep может вызывать исключение InterruptedException, мы его обрабатываем с помощью операторов try и catch. Если произошло исключение, мы завершаем задачу, вызывая метод stop.
Заметим, что периодическая перерисовка окна аплета может привести к неприятному миганию, поэтому использованный здесь метод периодического обновления содержимого окна аплета нельзя назвать оптимальным. Позже мы рассмотрим другой метод, при котором такой перерисовки не происходит.
Программный код метода run работает в рамках отдельной задачи. Он рисует в окне аплета закрашенные прямоугольники. Прямоугольники имеют случайные координаты, расположение и цвет.
Для того чтобы рисовать, необходимо получить контекст отображения. Так как наша задача, точнее, метод run определен в классе аплета, то он может получить контекст отображения, вызвав метод getGraphics:
Graphics g = getGraphics();
Для рисования нам также нужно знать размеры окна аплета. Мы получаем эти размеры при помощи метода size:
Dimension dimAppWndDimension = size();
Вооружившись контекстом отображения и размерами окна аплета, задача входит в бесконечный цикл рисования прямоугольников.
В качестве генератора случайных чисел мы используем метод random из класса Math, который при каждом вызове возвращает новое случайное число типа double, лежащее в диапазоне значений от 0.0 до 1.0.
Координаты по осям X и Y рисуемого прямоугольника определяются простым умножением случайного числа, полученного от метода random, соответственно, на ширину и высоту окна аплета:
x = (int)(dimAppWndDimension.width * Math.random());
y = (int)(dimAppWndDimension.height * Math.random());
Аналогично определяются размеры прямоугольника, однако чтобы прямоугольники не были слишком крупными, мы делим полученные значения на 2:
width = (int)(dimAppWndDimension.width * Math.random())/2;
height = (int)(dimAppWndDimension.height * Math.random())/2;
Так как случайное число имеет тип double, в обоих случаях мы выполняем явное преобразование результата вычислений к типу int.
Для случайного выбора цвета прямоугольника мы вычисляем отдельные цветовые компоненты, умножая значение, полученное от метода random, на число 255:
rColor = (int)(255 * Math.random());
gColor = (int)(255 * Math.random());
bColor = (int)(255 * Math.random());
Полученные значения цветовых компонент используются в конструкторе Color для получения цвета. Этот цвет устанавливается в контексте отображения методом setColor:
g.setColor(new Color(rColor, gColor, bColor));
Теперь все готово для рисования прямоугольника, которое мы выполняем при помощи метода fillRect:
g.fillRect(x, y, width, height);
После рисования прямоугольника метод run задерживает свою работу на 50 миллисекунд, вызывая метод sleep:
try
{
Thread.sleep(50);
}
catch (InterruptedException e)
{
stop();
}
Для обработки исключения InterruptedException, которое может возникнуть во время работы этого метода, мы предусмотрели блок try - catch. При возникновении указанного исключения работа задачи останавливается вызовом метода stop.
Внутри метода run мы определили массив строк, проинициализировав его значениями, полученными из параметров аплета:
String s[] = new String[6];
s[0] = m_String1;
s[1] = m_String2;
s[2] = m_String3;
s[3] = m_String4;
s[4] = m_String5;
s[5] = m_String6;
Задача, выполняющаяся в рамках метода run одновременно с кодом аплета, будет по очереди извлекать строки из этого массива и отображать их в нижней части окна аплета.
Так как для рисования строк текста нужно знать контекст отображения, мы получаем его при помощи метода getGraphics:
Graphics g = getGraphics();
Мы также определяем размеры окна аплета, знание которых необходимо для организации сдвига содержимого окна:
Dimension dimAppWndDimension = size();
Перед тем как запустить бесконечный цикл, мы также определяем метрики текущего шрифта и высоту символов шрифта:
FontMetrics fm = g.getFontMetrics();
yChar = fm.getHeight();
В рамках бесконечного цикла мы подсчитываем количество сдвигов (в счетчике ShiftsCounter), а также сдвинутые строки (в счетчике CurrentStr). Заметим, что для обеспечения плавности сдвига мы перемещаем строки по одному пикселу. Когда величина сдвига достигает высоты символов yChar плюс 5, метод run рисует новую строку.
Перед рисованием строки мы выбираем в контекст отображения красный или черный цвет, в зависимости от номера строки:
if(CurrentStr == 0)
g.setColor(Color.red);
else
g.setColor(Color.black);
Вы можете выделять нужные вам строки любым другим способом, например, наклоном или жирным шрифтом.
Для рисования строки мы вызываем метод drawString:
g.drawString(s[CurrentStr],
10, dimAppWndDimension.height - 10);
Строка будет нарисована на десять пикселов выше нижней границы окна аплета.
После рисования строки мы проверяем, последняя она, или нет:
CurrentStr++;
if(CurrentStr > 5)
CurrentStr = 0;
Если строка последняя, мы сбрасываем счетчик текущей строки, после чего перебор строк начнется с самого начала.
Для выполнения свертки мы вызываем метод copyArea, знакомый вам по 30 тому “Библиотеки системного программиста”:
yShift = 1;
g.copyArea(0, yShift + 1,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1,
0, -yShift);
Этот метод сдвигает содержимое прямоугольной области экрана, заданной первыми четырьмя параметрами. Величина сдвига определяются двумя последними параметрами метода. В нашем случае сдвиг выполняется по вертикальной оси на значение -1, то есть на один пиксел вверх.
После сдвига освободившаяся область закрашивается желтым цветом:
g.setColor(Color.yellow);
g.fillRect(1, dimAppWndDimension.height - yShift - 1,
dimAppWndDimension.width - 2,
dimAppWndDimension.height - 1);
Далее выполняется задержка на 50 миллисекунд, после чего работа бесконечного цикла возобновляется с самого начала:
Thread.sleep(50);
Перед запуском бесконечного цикла отображения символов строки метод run получает контекст отображения и устанавливает в нем параметры шрифта в соответствии со значениями, переданными аплету через документ HTML.
Прежде всего, метод run получает контекст отображения:
Graphics g = getGraphics();
Затем в этом контексте отображения устанавливается шрифт с жирным, наклонным или обычным начертанием:
if(m_style.equals("BOLD"))
g.setFont(new Font(m_Fnt, Font.BOLD, m_size));
else if(m_style.equals("ITALIC"))
g.setFont(new Font(m_Fnt, Font.ITALIC, m_size));
else
g.setFont(new Font(m_Fnt, Font.PLAIN, m_size));
Обратите внимание, что название шрифта передается конструктору класса Font через первый параметр, а размер символов - через последний.
В зависимости от содержимого поля m_color метод run устанавливает один из трех цветов для отображения символов текстовой строки:
if(m_color.equals("red"))
g.setColor(Color.red);
else if(m_color.equals("green"))
g.setColor(Color.green);
else
g.setColor(Color.black);
Помимо этого, до запуска цикла метод run получает размеры окна аплета и метрики шрифта, только что установленного в контексте отображения:
Dimension dimAppWndDimension = size();
FontMetrics fm = g.getFontMetrics();
В переменную nCurrentChar, хранящую номер текущего отображаемого символа, записывается нулевое значение.
Кроме того, вычисляется позиция для рисования строки по вертикальной оси yPos и устанавливается начальная позиция первого символа строки по горизонтальной оси nCurrentXPos:
int yPos = fm.getHeight() + 5;
int nCurrentXPos = 10;
Далее метод run запускает бесконечный цикл рисования символов.
Первое, что метод run делает в этом цикле, это вычисление ширины текущего символа, сохраняя ее в переменной nCurrentCharWidth:
nCurrentCharWidth =
fm.charWidth(m_Str.charAt(nCurrentChar));
Текущий символ извлекается из строки при помощи метода charAt, определенном в классе String. Ширина извлеченного таким образом символа символа определяется методом charWidth из класса метрик шрифта FontMetrics.
Метод run работает в рамках отдельной задачи. Он занимается последовательным рисованием кадров нашего видеофильма.
Прежде всего метод run записывает нулевое значение в поле m_nCurrImage, хранящее номер текущего отображаемого кадра:
m_nCurrImage = 0;
Далее выполняется проверка, загружены ли все кадры видеофильма, для чего анализируется содержимое флага m_fAllLoaded.
Если изображения не загружены (а в самом начале так оно и есть) метод run перерисовывает окно аплета и получает контекст отображения для этого окна. Затем создается массив объектов Image для хранения кадров видеофильма:
m_Images = new Image[NUM_IMAGES];
Метод run создает также объект класса MediaTracker для ожидания загрузки всех кадров видеофильма:
MediaTracker tracker = new MediaTracker(this);
Далее метод run в цикле загружает изображения и добавляет их в объект класса MediaTracker для того чтобы можно было дождаться загрузки всех кадров:
for (int i = 0; i < NUM_IMAGES; i++)
{
strImage = "images/cdimg0" + ((i < 10) ? "0" : "") +
i + ".gif";
m_Images[i] = getImage(getDocumentBase(), strImage);
tracker.addImage(m_Images[i], 0);
}
Здесь предполагается, что файлы изображений находятся в каталоге images, который, в свою очередь, размещен там же, где и двоичный файл аплета.
Имена файлов, составляющих отдельные кадры, начинаются с префикса cdimg0, вслед за которым идет номер кадра (00, 01, 02, и так далее), и расширение имени .gif.
Ожидание загрузки кадров выполняется с помощью метода waitForAll, о котором мы вам уже рассказывали:
try
{
tracker.waitForAll();
m_fAllLoaded = !tracker.isErrorAny();
}
catch (InterruptedException e)
{
}
После окончания ожидания флаг завершения загрузки устанавливается только в том случае, если метод isErrorAny вернул значение false, то есть если не было никаких ошибок.
Если же произошла ошибка, в окне аплета отображается соответствующее сообщение, после чего работа метода run (и, следовательно, работа созданной для него задачи) заканчивается:
Метод run класса DrawRectangles
Код метода run выполняется в рамках отдельной задачи. Так как он аналогичен коду метода run предыдущего приложения, то для экономии места мы не будем его описывать.
Метод run вызывает метод wait для синхронизации с другой задачей, поэтому этот метод определен как синхронизированный с помощью ключевого слова synchronized:
public synchronized void run()
{
. . .
}
Внутри метода run организован цикл рисования, который мы уже описывали. После рисования очередного прямоугольника метод run переходит в состояние ожидания извещения, вызывая метод wait:
try
{
Thread.wait();
}
catch (InterruptedException e)
{
}
Метод run класса NotifyTask
Метод run класса NotifyTask периодически разблокирует задачу рисования прямоугольников, вызывая для этого метод notify в цилке с задержкой 30 миллисекунд. Обращение к объекту STask, который хранит ссылку на задачу рисования прямоугольников, выполняется с использованием синхронизации:
synchronized(STask)
{
STask.notify();
}
Метод sameFile
С помощью метода sameFile вы можете определить, ссылаются ли два объекта класса URL на один и тот же ресурс, или нет:
public boolean sameFile(URL other);
Если объекты ссылаются на один и тот же ресурс, метод sameFile возвращает значение true, если нет - false.
Метод start
Метод start вызывается, когда пользователь отображает документ HTML, содержащий аплет. Наша реализация этого метода проверяет, создана ли задача перерисовки окна, и, если эта задача не запущена, создает и запускает ее:
public void start()
{
if(m_MultiTask == null)
{
m_MultiTask = new Thread(this);
m_MultiTask.start();
}
}
Первоначально в поле m_MultiTask находится значение null, поэтому при первом вызове метода start всегда создается задача как объекта класса Thread. При этом конструктору с помощью ключевого слова this передается ссылка на наш аплет, поэтому при запуске задачи управление будет передано методу run, определенному в аплете.
Созданная задача не запускается автоматически. Для запуска необходимо вызвать метод start.
Когда пользователь начинает просмотр документа HTML, содержащего наш аплет, метод start создает и запускает задачу. Для создания задачи мы используем оператор new, а для старта задачи - метод start класса Thread:
public void start()
{
if (m_Rectangles == null)
{
m_Rectangles = new Thread(this);
m_Rectangles.start();
}
}
Обратите внимание, что мы передаем конструктору класса Thread параметр this - ссылку на аплет. В результате роль задачи, работающей параллельно с кодом аплета, будет выполнять метод run, определенный в классе аплета.
Ссылка на созданную задачу записывается в поле m_Rectangles.
Метод start основного класса аплета вызывается, когда пользователь отображает страницу сервера Web с аплетом. Наша реализация этого метода создает новую задачу и сохраняет ссылку на нее в поле m_Scroller.
В задачу метода start, который получает управление при отображении окна аплета, входит создание и запуск задачи, отображающий кадры видеофильма с изображением вращающегося компакт-диска:
if (m_CDRotation == null)
{
m_CDRotation = new Thread(this);
m_CDRotation.start();
}
Задача создается как объект класса Thread, причем конструктору передается ссылка на главный класс аплета. Поэтому при запуске задачи управление получит метод run, определенный в классе аплета.
Метод start получает управление при первом запуска аплета, а также когда страница документа появляется вновь после того как пользователь временно переходил к просмотру другой страницы.
Наша реализация метода start возобновляет циклическое проигрывание, если оно выполнялось, когда пользователь покинул страницу с аплетом:
if(fLoopPlay)
auClip.loop();
Этот метод последовательно создает две
Этот метод последовательно создает две задачи и запускает их на выполнение:
public void start()
{
if (m_DrawRectThread == null)
{
m_DrawRectThread = new DrawRectangles(this);
m_DrawRectThread.start();
}
if (m_DrawEllipseThread == null)
{
m_DrawEllipseThread = new DrawEllipse(this);
m_DrawEllipseThread.start();
}
}
Метод start основного класса
Метод start создает и запускает на выполнение две задачи. Первая задача создается как объект класса DrawRectangles, вторая - как объект класса NotifyTask:
if (m_DrawRectThread == null)
{
m_DrawRectThread = new DrawRectangles(this);
m_DrawRectThread.start();
}
if (m_NotifyTaskThread == null)
{
m_NotifyTaskThread = new NotifyTask(m_DrawRectThread);
m_NotifyTaskThread.start();
}
При создании задачи рисования прямоугольников конструктору передается ссылка на аплет. Эта ссылка нужна задаче для определения размеров окна аплета и получения контекста отображения.
Конструктору класса NotifyTask передается ссылка на задачу, работой которой она будет управлять с помощью механизма ожидания извещений.
Метод stop
Когда пользователь покидает страницу с аплетом, имеет смысл остановить нашу задачу, чтобы она не отнимала ресурсов процессора. Остановка выполняется с помощью метода stop:
public void stop()
{
if(m_MultiTask != null)
{
m_MultiTask.stop();
m_MultiTask = null;
}
}
После остановки мы записываем в поле m_MultiTask значение null.
Метод stop нашего аплета не имеет никаких особенностей. Он вызывается, когда пользователь покидает страницу сервера Web с аплетом. В этом случае метод останавливает задачу, вызывая для этого метод stop класса Thread:
public void stop()
{
if (m_Rectangles != null)
{
m_Rectangles.stop();
m_Rectangles = null;
}
}
После остановки в поле m_Rectangles записывается значение null. Это является признаком того, что задача остановлена.
Метод stop основного класса останавливает работу задачи, когда пользователь покидает страницу сервера Web с аплетом, вызывая для этого метод stop.
Метод stop останавливает работу задачи, когда окно аплета исчезает с экрана:
if(m_CDRotation != null)
{
m_CDRotation.stop();
m_CDRotation = null;
}
Для остановки вызывается метод stop.
Если пользователь запустил проигрывание звукового файла в цикле, а затем перешел к просмотру другой страницы, метод stop останавливает циклическое проигрывание:
if(fLoopPlay)
auClip.stop();
Когда пользователь вернется к просмотру нашей страницы, метод start, описанный выше, возобновит проигрывание звукового файла.
Когда пользователь покидает страницу сервера
Когда пользователь покидает страницу сервера Web с аплетом, метод stop класса MultiTask2 последовательно останавливает задачи рисования прямоугольников и эллипсов:
public void stop()
{
if (m_DrawRectThread != null)
{
m_DrawRectThread.stop();
m_DrawRectThread = null;
}
if (m_DrawEllipseThread == null)
{
m_DrawEllipseThread.stop();
m_DrawEllipseThread = null;
}
}
Метод stop основного класса
Когда пользователь покидает страницу с аплетом, метод stop останавливает работу обеих задач, вызывая для них метод stop из класса Thread:
if (m_DrawRectThread != null)
{
m_DrawRectThread.stop();
m_DrawRectThread = null;
}
if (m_NotifyTaskThread != null)
{
m_NotifyTaskThread.stop();
m_NotifyTaskThread = null;
}
Метод toExternalForm
Метод toExternalForm возвращает текстовую строку внешнего представления адреса URL, определенного данным объектом класса URL:
public String toExternalForm();
Метод toString
Метод toString возвращает текстовую строку, представляющую данный объект класса URL:
public String toString();
Методы для чтения и записи форматированных данных
Вместо того чтобы записывать в потоки и читать оттуда отдельные байты или массивы байт, программисты обычно предпочитают пользоваться намного более удобными методами классов DataOutputStream и DataInputStream, допускающими форматированный ввод и вывод данных.
Вот, например, какой набор методов можно использовать для записи форматированных данных в поток класса DataOutputStream:
public final void writeBoolean(boolean v);
public final void writeByte(int v);
public final void writeBytes(String s);
public final void writeChar(int v);
public final void writeChars(String s);
public final void writeDouble(double v);
public final void writeFloat(float v);
public final void writeInt(int v);
public final void writeLong(long v);
public final void writeShort(int v);
public final void writeUTF(String s);
Хотя имена методов говорят сами за себя, сделаем замечания относительно применения некоторых из них.
Метод writeByte записывает в поток один байт. Это младший байт слова, которое передается методу через параметр v. В отличие от метода writeByte, метод writeChar записывает в поток двухбайтовое символьное значение (напомним, что в Java символы хранятся с использованием кодировки Unicode и занимают два байта).
Если вам нужно записать в выходной поток текстовую строку, то это можно сделать с помощью методов writeBytes, writeChars или writeUTF. Первый из этих методов записывает в выходной поток только младшие байты символов, а второй - двухбайтовые символы в кодировке Unicode. Метод writeUTF предназначен для записи строки в машинно-независимой кодировке UTF-8.
Все перечисленные выше методы в случае возникновения ошибки создают исключение IOException, которое вы должны обработать.
В классе DataInputStream определены следующие методы, предназначенные для чтения форматированных данных из входного потока:
public final boolean readBoolean();
public final byte readByte();
public final char readChar();
public final double readDouble();
public final float readFloat();
public final void readFully(byte b[]);
public final void readFully(byte b[], int off, int len);
public final int readInt();
public final String readLine();
public final long readLong();
public final short readShort();
public final int readUnsignedByte();
public final int readUnsignedShort();
public final String readUTF();
public final static String readUTF(DataInput in);
public final int skipBytes(int n);
Обратите внимание, что среди этих методов нет тех, что специально предназначены для четния данных, записанных из строк методами writeBytes и writeChars класса DataOutputStream.
Тем не менее, если входной поток состоит из отдельных строк, разделенных символами возврата каретки и перевода строки, то такие строки можно получить методом readLine. Вы также можете воспользоваться методом readFully, который заполняет прочитанными данными массив байт. Этот массив потом будет нетрудно преобразовать в строку типа String, так как в классе String предусмотрен соответствующий конструктор.
Для чтения строк, записанных методом writeUTF вы должны обязательно пользоваться методом readUTF.
Метод skipBytes позволяет пропустить из входного потока заданное количество байт.
Методы класса DataInputStream, предназначенные для чтения данных, могут создавать исключения IOException и EOFException. Первое из них возникает в случае ошибки, а второе - при достижении конца входного потока в процессе чтения.
Методы для настройки параметров разборщика
Ниже мы привели прототипы методов, предназначенных для настройки параметров разборщика:
public void commentChar(int ch);
public void slashSlashComments(boolean flag);
public void slashStarComments(boolean flag);
public void quoteChar(int ch);
public void eolIsSignificant(boolean flag);
public void lowerCaseMode(boolean fl);
public void ordinaryChar(int ch);
public void ordinaryChars(int low, int hi);
public void resetSyntax();
public void parseNumbers();
public void whitespaceChars(int low, int hi);
public void wordChars(int low, int hi);
Несколько методов определяют, будет ли разборщик выделять во входном потоке строки комментария и если будет, то какми образом.
С помощью метода commentChar вы можете указать символ комментария. Если в строке входного потока попадется такой символ, то он и все следующие за ним до конца текущей строки символы будут проигнорированы.
Методы SlashSlashComments и slashStarComments позволяют указать, что для входного текста используются разделители комментариев в виде двойного символа ‘/’ и ‘/* … */’, соответственно. Это соответствует способу указания комментариев в программах, составленных на языках программирования С++ и С. Для включения режима выделения комментариев обоим методам в качетстве параметра необходимо передать значение true, а для отключения - false.
Метод quoteChar позволяет задать символ, который будет использован в качестве кавычек. Когда при разборе потока встречаются слова, взятые в кавычки, они возвращаются программе разбора без кавычек.
Если передать методу eolIsSignificant значение true, разделители строк будут интерпретироваться как отдельные элементы. Если же этому методу передать значение false, разделители строк будут использоваться аналогично пробелам для разделения элементов входного потока.
Метод lowerCaseMode позволяет включить режим, при котором все выделенные элементы будут перекодированы в строчные символы.
Методы ordinaryChar и ordinaryChars позволяют указать символы, которые должны интерпретироваться как обычные, из которых составляются слова или цифры. Например, если передать методу ordinaryChar символ ‘.’, то слово java.io будет восприниматься как один элемент. Если же этого не сделать, то разборщик выделит из него три элемента - слово java, точку ‘.’ и слово io. Метод ordinaryChars позволяет указать диапазон значений символов, которые должны интерпретироваться как обычные.
С помощью метода resetSyntax вы можете указать, что все символы будут рассматриваться, как обычные.
Метод parseNumbers включает режим разбора чисел, при котором распознаются и преобразуются числа в формате с плавающей десятичной точкой.
Метод whitespaceChars задает диапазон значений для символов-разделителей отдельных слов в потоке.
Метод wordChars позволяет указать символы. Которые являются составными частями слов.
Методы для разбора входного потока
После того как вы создали разборщик входного потока на базе класса StreamTokenizer и установили его параметры с помощью описанных выше методов, можно приступать собственно к разборке потока. Обычно для этого организуется цикл, в котором вызывается метод nextToken:
public int nextToken();
Этот метод может вернуть одно из следующих значений:
Значение | Описание | ||
TT_WORD | Из потока было извлечено слово | ||
TT_NUMBER | Из потока было извлечено численное значение | ||
TT_EOL | Обнаружен конец строки. Это значение возвращается только в том случае, если при настройке параметров разборщика был вызван метод eolIsSignficant | ||
TT_EOF | Обнаружен конец файла |
Если метод nextToken вернул значение TT_EOF, следует завершить цикл разбора входного потока.
Как извлечь считанные элементы потока?
В классе StreamTokenizer определено три поля:
public String sval;
public double nval;
public int ttype;
Если метод nextToken вернул значение TT_WORD, в поле sval содержится извлеченный элемент в виде текстовой строки. В том случае, когда из входного потока было извлечено числовое значение, оно будет храниться в поле nval типа double. Обычные символы записываются в поле ttype.
Заметим, что если в потоке обнаружены слова, взятые в кавычки, то символ кавычки записывается в поле ttype, а слова - в поле sval. По умолчанию используется символ кавычек ‘”’, однако с помощью метода quoteChar вы можете задать любой другой символ.
При необходимости в процессе разбора вы можете определить номер текущей строки, вызвав для этого метод lineno:
public int lineno();
После вызова метода pushBack следующий вызов метода nextToken приведет к тому, что в поле ttype будет записано текущее значение, а содержимое полей sval и nval не изменится. Прототип метода pushBack приведен ниже:
public void pushBack();
Метод toString возвращает текстовую строку, представляющую текущий элемент, выделенный из потока:
public String toString();
Методы класса Socket
Перечислим наиболее интересные, на наш взгляд, методы класса Socket.
Прежде всего, это методы getInputStream и getOutputStream, предназначенные для создания входного и выходного потока, соответственно:
public InputStream getInputStream();
public OutputStream getOutputStream();
Эти потоки связаны с сокетом и должны быть использованы для передачи данных по каналу связи.
Методы getInetAddress и getPort позволяют определить адрес IP и номер порта, связанные с данным сокетом (для удаленного узла):
public InetAddress getInetAddress();
public int getPort();
Метод getLocalPort возвращает для данного сокета номер локального порта:
public int getLocalPort();
После того как работа с сокетом завершена, его необходимо закрыть методом close:
public void close();
И, наконец, метод toString возвращает текстовую строку, представляющую сокет:
public String toString();
Методы класса StreamTokenizer
Для настройки параметров разборщика StreamTokenizer и получения отдельных элементов входного потока вы должны пользоваться методами, определенными в классе StreamTokenizer. Рассмотрим самые важние из них.
Методы класса Thread
В классе Thread определены три поля, несколько конструкторов и большое количество методов, предназначенных для работы с задачами. Ниже мы привели краткое описание полей, конструкторов и методов.
public class java.lang.Thread
extends java.lang.Object
implements java.lang.Runnable
{
// -----------------------------------------------------
// Поля
// -----------------------------------------------------
// Приоритеты задач
public final static int NORM_PRIORITY; // нормальный
public final static int MAX_PRIORITY; // максимальный
public final static int MIN_PRIORITY; // минимальный
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание нового объекта Thread
public Thread();
// Создвание нового объекта Thread с указанием объекта,
// для которого будет вызываться метод run
public Thread(Runnable target);
// Аналогично предыдущему, но дополнительно задается
// имя нового объекта Thread
public Thread(Runnable target, String name);
// Создание объекта Thread с указанием его имени
public Thread(String name);
// Создание нового объекта Thread с указанием группы
// задачи и объекта, для которого вызывается метод run
public Thread(ThreadGroup group, Runnable target);
// Аналогично предыдущему, но дополнительно задается
// имя нового объекта Thread
public Thread(ThreadGroup group, Runnable target,
String name);
// Создание нового объекта Thread с указанием группы
// задачи и имени объекта
public Thread(ThreadGroup group, String name);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Текущее количество активных задач в группе, к которой
// принадлежит задача
public static int activeCount();
// Текущей задаче разрешается изменять объект Thread
public void checkAccess();
// Определение количества фреймов в стеке
public int countStackFrames();
// Определение текущей работающей задачи
public static Thread currentThread();
// Принудительное завершение работы задачи
public void destroy();
// Вывод текущего содержимого стека для отладки
public static void dumpStack();
// Получение всех объектов Tread данной группы
public static int enumerate(Thread tarray[]);
// Определение имени задачи
public final String getName();
// Определение текущего приоритета задачи
public final int getPriority();
// Определение группы, к которой принадлежит задача
public final ThreadGroup getThreadGroup();
// Прерывание задачи
public void interrupt();
// Определение, является ли задача прерванной
public static boolean interrupted();
// Определение, выполняется задача или нет
public final boolean isAlive();
// Определение, является ли задача демоном
public final boolean isDaemon();
// Определение, является ли задача прерванной
public boolean isInterrupted();
// Ожидание завершения задачи
public final void join();
// Ожидание завершения задачи в течение заданного времени.
// Время задается в миллисекундах
public final void join(long millis);
// Ожидание завершения задачи в течение заданного времени.
// Время задается в миллисекундах и наносекундах
public final void join(long millis, int nanos);
// Запуск временно приостановленной задачи
public final void resume();
// Метод вызывается в том случае, если задача была
// создана как объект с интерфейсом Runnable
public void run();
// Установка для задачи режима демона
public final void setDaemon(boolean on);
// Устаовка имени задачи
public final void setName(String name);
// Установка приоритета задачи
public final void setPriority(int newPriority);
// Задержка задачи на заднное время.
// Время задается в миллисекундах и наносекундах
public static void sleep(long millis);
// Задержка задачи на заднное время.
// Время задается в миллисекундах и наносекундах
public static void sleep(long millis, int nanos);
// Запуск задачи на выполнение
public void start();
// Остановка выполнения задачи
public final void stop();
// Аварийная остановка выполнения задачи с
// заданным исключением
public final void stop(Throwable obj);
// Приостановка задачи
public final void suspend();
// Строка, представляющая объект-задачу
public String toString();
// Приостановка текущей задачи для того чтобы
// управление было передано другой задаче
public static void yield();
}
С помощью конструкторов вы можете создавать задачи различными способами, указывая при необходимости для них имя и группу. Имя предназначено для идентификации задачи и является необязательным атрибутом. Что же касается групп, то они предназначены для организации защиты задач друг от друга в рамках одного приложения. Подробнее мы расскажем об этом позже.
Методы класса Thread предоставляют все необходимые возможности для управления задачами, в том числе для их синхронизации. Более подробное описание этих методов мы будем приводить по мере изложения материала.
Методы класса URL
Рассмотрим самые интересные методы, определенные в классе URL.
Обращение к полям и методам других аплетов
Теперь вы научились искать аплеты, расположенные в текущем документе HTML, получая список ссылок на соответствующие объекты. Однако для того чтобы получить доступ к полям и методам найденных аплетов, вы должны сделать еще одну вещь. Нужно импортировать в аплет, который занимается поиском, описание класса аплета, к полям и методам которого будет выполняться обращение.
Поясним это.
Все аплеты, как вы знаете, происходят от класса Applet. Они добавляют в этот класс свои поля и методы, а также переопределяют методы из базового класса.
В процессе поиска аплетов метод nextElement возвращает ссылку на объект, принадлежащий к классу Object, который мы можем преобразовать к классу Applet:
Applet currentApplet = (Applet)(eApplets.nextElement());
Однако такое преобразование сможет открыть нам доступ только к тем полям и методам, которые определены в базовом классе. Для того чтобы получить доступ к полям и методам класса найденного аплета, мы должны преобразовать значение, полученное от метода nextElement, к ссылке на класс этого аплета.
Как это можно сделать?
Рассмотрим конкретный пример, использованный нами в приложении Inspector, полные исходные тексты которого вы найдете ниже.
Это приложение управляет работой аплета Audio, описанного в предыдущей главе и предназначенного для проигрывания звукового файла. В классе Audio определено поле auClip, в котором хранится ссылка на интерфейс AudioClip:
public class Audio extends Applet
{
private String m_ClipName = "kaas.au";
private final String PARAM_ClipName = "ClipName";
AudioClip auClip;
. . .
}
Аплет Inspector получает доступ к полю auClip и вызывает методы, предназначенные для управления проигрыванием звукового файла. Таким образом, аплет Inspector пользуется полем auClip, определенным в другом аплете.
Чтобы это стало возможным, в исходном тексте аплета Inspector импортируется класс Audio, как это показано ниже:
import java.applet.*;
import java.awt.*;
import java.util.*;
import Audio;
Когда в процессе поиска аплетов аплет Inspector обнаруживает аплет Audio, он сохраняет ссылку на этот аплет в поле appAudio, выполняя явное преобразование типов:
Audio appAudio = null;
. . .
if(appName.equals("Name: Audio"))
{
appAudio = (Audio)currentApplet;
}
Теперь, пользуясь значением из поля appAudio, можно обращаться к полю auClip, определенному в аплете Audio:
appAudio.auClip.play();
В этом приложении мы создаем
В этом приложении мы создаем на базе класса Thread два класса, один из которых предназначен для создания задачи рисования прямоугольников, а другой - для создания задачи рисования закрашенных эллипсов.
Что же касается основного класса аплета, то он унаследован, как обычно, от класса Applet и не реализует интерфейс Runnable.
Структура приложения Standard очень проста. В нем определен один класс с именем Standard типа public, и один метод с имененм main:
public class Standard
{
public static void main(String args[])
{
. . .
}
}
Напомним, что имена класса и файла .class должны совпадать.
Сразу после запуска автономного приложения Java управление передается функции main.
Внутри этой функции мы определили массив bKbdInput типа byte и строку sOut:
byte bKbdInput[] = new byte[256];
String sOut;
Созданный оператором new массив имеет размер 256 байт и предназначен для хранения строки, введенной пользователем при помощи клавиатуры. В дальнейшем содержимое этого массива преобразуется в строку sOut.
Первое, что делает наше приложение после создания массива, это вывод на консоль текстовой строки приглашения:
System.out.println("Hello, Java!\n" +
"Enter string and press <Enter>...");
Здесь вызывается метод println для статического объекта out класса PrintStream, который, как вы знаете, определен в классе System.
На следующем этапе приложение читает из стандартного потока ввода in, вызывая для этого метод read:
System.in.read(bKbdInput);
Стандартный поток ввода связан с клавиатурой, поэтому приложение перейдет в состояние ожидания до тех пор, пока пользователь не введет текстовую строку, нажав после чего клавишу <Enter>.
Введенная строка отображается на консоли, для чего она записывается в стандартный поток вывода методом println:
System.out.println(sOut);
При выполнении операций с потоками ввода или вывода могут возникать исключения, поэтому в нашем приложении предусмотрены обработчики исключений:
catch(Exception ioe)
{
System.err.println(ioe.toString());
}
При возникновении любого исключения в стандартный поток вывода сообщений об ошибках записывается текстовая строка названия класса возникнувшего исключения.
Для того чтобы вы смогли посмотреть на результаты работы приложения, после отображения на консоли введенной строки приложение вновь вызывается метод read для стандартного потока ввода. Для завершения работы приложения пользователь должен нажать клавишу <Enter>.
После ввода текстовой строки и ее записи в поле sOut наше приложение создает на базе этой строки объект st класса StringTokenizer:
StringTokenizer st;
st = new StringTokenizer(sOut, ",.; ");
Далее он в цикле получает все элементы строки, вызывая для этого метод nextElement:
while(st.hasMoreElements())
{
str = new String((String)st.nextElement());
System.out.println(str);
}
Для проверки условия завершения цикла вызывается метод hasMoreElements. Когда он возвращает значение false, цикл завершается.
Выделенные в цикле элементы строки записываются в переменную str и отображаются на консоли.
После ввода с клавиатуры пути к файлу или каталогу приложение записывает введенный путь в строку sFilePath класса String.
Так как в этой строке имеется символ конца строки, нам нужно его отрезать. Для этого мы воспользуемся классом StringTokenizer, задав для него в качестве разделителя символ конца строки:
StringTokenizer st;
st = new StringTokenizer(sFilePath, "\r\n");
sFilePath = new String((String)st.nextElement());
Первый же вызов метода nextElement возвращает нам строку пути, которую мы и сохраняем в поле sFilePath.
Далее мы создаем объект класса File, передавая конструктору этого класса строку sFilePath:
File fl = new File(sFilePath);
Так как при вводе пути файла или каталога вы можете допустить ошибку, приложение, прежде чем продолжать свою работу, проверяет существование указанного файла или каталога. Для проверки вызывается метод exists, определенный в классе File:
if(!fl.exists())
{
System.out.println("File not found: " + sFilePath);
}
На следующем этапе приложение проверяет, является ли объект класса File каталогом, вызвая метод isDirectory:
if(fl.isDirectory())
System.out.println("File " + sFilePath + " is directory");
Аналогичная проверка выполняется методом isFile на принадлежность объекта к файлам:
else if (fl.isFile())
System.out.println("File " + sFilePath + " is file");
На последнем этапе приложение определяет различные атрибуты файла или каталога, вызывая соответствующие методы класса File:
System.out.println(
"Parent: " + fl.getParent() +
"\nLength: " + fl.length() +
"\nRead op. available: " + fl.canRead() +
"\nWrite op. available: " + fl.canWrite());
Параметры отображаются на консоли методом println.
В начале своей работы приложение вводит с клавиатуры путь к каталогу и отрезает из полученной строки символ новой строки, пользуясь для этого классом StringTokenizer:
System.out.println("Enter directory path...");
System.in.read(bKbdInput);
sDirPath = new String(bKbdInput, 0);
StringTokenizer st;
st = new StringTokenizer(sDirPath, "\r\n");
sDirPath = new String((String)st.nextElement());
Строка пути записывается в поле sDirPath.
Аналогичным образом вводится и обрабатывается маска, которая записывается в поле sMask.
Далее создается объект класса File, соответствующий каталогу sDirPath, содержимое которого нужно просмотреть:
File fdir = new File(sDirPath);
После этого выполняется проверка существования пути, а также проверка, указывает ли этот путь на каталог. Для проверки мы применяем методы exists и isDirectory, рассмотренные ранее.
Если все нормально, и был указан существующий каталог, приложение анализирует поле маски sMask. В случае пустой маски для получения содержимого каталога мы вызваем метод list без параметров:
if(sMask == null)
dirlist = fdir.list();
Если же маска определена, вызывается второй вариант этого же метода:
else
dirlist = fdir.list(new MaskFilter(sMask));
Здесь в качестве параметра методу list мы передаем вновь созданный объект класса MaskFilter (фильтр), передав соответствующему конструктору строку маски.
В любом случае метод list заполняет полученным списком массив строк dirlist. Содержимое этого массива перебирается в цикле:
for (int i = 0; i < dirlist.length; i++)
{
File f = new File(sDirPath + "\\" + dirlist[i]);
if(f.isFile())
System.out.println(dirlist[i].toLowerCase());
else
System.out.println(dirlist[i]);
}
Для каждого элемента массива мы создаем объект класса File, передавая конструктору путь каталога, добавив к нему разделитель и строку элемента массива. Затем если данная строка соответсвует файлу, а не каталогу, имя выводится строчными буквами. Для преобразования мы вызываем метод toLowerCase, определенный в классе String.
Сразу после запуска приложение запрашивает с консоли текстовую строку адреса URL файла, который необходимо переписать через сеть на локальный диск. После удаления символа перевода строки адрес записывается в поле sURL.
Далее приложение создает объект класса URL, соответствующий введенному адресу:
u = new URL(sURL);
На следующем этапе для объекта URL создается входной поток, для чего вызывается метод openStream:
InputStream is = u.openStream();
Идентификатор этого потока сохраняется в поле is.
Принятый файл будет записан в текущий каталог под именем output.dat. Для этого мы создаем входной буферизованный форматированный поток os, как это показано ниже:
DataOutputStream os = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.dat")));
После знакомства с главой нашей книги, посвященной работе с потоками, эти строки не должны вызывать у вас никаких вопросов.
Операция чтения данных из входного потока и записи в выходной поток выполняется в цикле:
while(true)
{
int nReaded = is.read(buf);
if(nReaded == -1)
break;
os.write(buf, 0, nReaded);
}
Вначале для входного потока вызывается метод read. Он возвращает количество прочитанных байт данных или значение -1, если был достигнут конец потока. В последнем случае цикл прерывается.
Принятые данные размещаются в массиве buf, откуда затем они записываются в выходной поток методом write. Мы записываем в выходной поток столько байт данных, сколько было считано.
После того как файл будет принят и записан в выходной поток, мы закрываем оба потока:
is.close();
os.close();
Приложение ShowChart получает содержимое файла исходных данных для построения круговой диаграммы с помощью класса URL. Как вы увидите, для получения содержимого этого файла оно не создает поток ввода явным образом, как это делало предыдущее приложение (хотя могло бы). Вместо этого оно пользуется методом getContent, определенным в классе URL.
В главном классе аплета определено несколько полей и методов. Рассмотрим эти поля и наиболее важные методы.
Описание исходного текста клиентского приложения SocketClient
Внутри метода main клиентского приложения SocketClient определены переменные для ввода строки с клавиатуры (массив bKbdInput), сокет s класса Socket для работы с сервером SocketServ, входной поток is и выходной поток os, которые связаны с сокетом s.
После вывода на консоль приглашающей строки клиентское приложение создает сокет, вызывая конструктор класса Socket:
s = new Socket("localhost",9999);
В процессе отладки мы запускали сервер и клиент на одном и том же узле, поэтому в качестве адреса сервера указана строка “localhost”. Номер порта сервера SocketServ равен 9999, поэтому мы и передали конструктору это значение.
После создания сокета наше клиентское приложение создает входной и выходной потоки, связанные с этим сокетом:
is = s.getInputStream();
os = s.getOutputStream();
Теперь клиентское приложение готово обмениваться данными с сервером.
Этот обмен выполняется в цикле, условием завершения которого является ввод пользователем строки “quit”.
Внутри цикла приложение читает строку с клавиатуры, записывая ее в массив bKbdInput:
length = System.in.read(bKbdInput);
Количество введенных символов сохраняется в переменной length.
Далее если пользователь ввел строку, а не просто нажал на клавишу <Enter>, эта строка отображается на консоли и передается серверу:
os.write(bKbdInput, 0, length);
os.flush();
Сразу после передачи сбрасывается буфер выходного потока.
Далее приложение читает ответ, посылаемый сервером, в буфер buf:
length = is.read(buf);
Напомним, что наш сервер посылает клиенту принятую строку в неизменном виде.
Если сервер закрыл канал, то метод read возвращает значение -1. В этом случае мы прерываем цикл ввода и передачи строк:
if(length == -1)
break;
Если же ответ сервера принят успешно, принятые данные записываются в строку str, которая отображается на консоли клиента:
System.out.println(">> " + str);
Перед завершением своей работы клиент закрывает входной и выходной потоки, а также сокет, на котором выполнялась передача данных:
is.close();
os.close();
s.close();
Описание исходного текста приложения
Внутри метода main мы создали ссылки на выходной поток OutStream и входной поток InStream:
DataOutputStream OutStream;
DataInputStream InStream;
Кроме того, мы предусмотрели массив для ввода информации из стандартного входного потока:
byte bKbdInput[] = new byte[256];
Способ вывода приглашения и получения строки с клавиатуры ничем не отличается от использованного в предыдущем приложении, поэтому для экономии места мы не будем на нем останавливаться. Скажем только, что введенная строка записывается в поле sOut типа String.
Создание выходного потока для записи строки выполняется следующим образом:
OutStream = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.txt")));
Вначале с помощью конструктора создается объект класса FileOutputStream - поток, связанный с файлом output.txt. Далее на базе этого потока создается буферизованный поток типа BufferedOutputStream. И, наконец, на базе буферизованного потока создается форматированный поток OutStream класса DataOutputStream.
Заметим, что конструктор класса FileOutputStream создает файл output.txt, если он не существует, и перезаписывает существующий файл. Если вам нужно исключить случайную перезапись существующего файла, необходимо воспользоваться классом File, о котором мы еще будем рассказывать.
Для записи строки sOut в выходной поток мы вызываем метод writeBytes:
OutStream.writeBytes(sOut);
После записи выполняется сброс содержимого буферов и закрытие потока:
OutStream.flush();
OutStream.close();
При закрытии потока содержимое буферов сбрасывается автоматически. Мы вызвали метод flush только для иллюстрации.
При работе с потоками могут возникать различные исключения. Наш обработчик этих исключений просто записывает в стандратный поток вывода название возникшего исключения:
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
На следующем этапе приложение открывает файл output.txt для чтения буферизованным форматированным потоком:
InStream = new DataInputStream(
new BufferedInputStream(
new FileInputStream("output.txt")));
В этой последовательности создания трех объектов различных классов для вас нет ничего нового. Вначале мы создаем поток ввода, связанный с файлом, затем на его базе - буферизованный входной поток, и, наконец, на базе буферизованного входного потока - буферизованный форматированный входной поток.
Для чтения строки из входного потока мы применили метод readLine:
System.out.println(InStream.readLine());
Прочитанная строка сразу отображается на консоли, для чего она записывается в стандартный поток вывода.
После завершения работы со входным потоком мы закрываем его методом close:
InStream.close();
На последнем этапе приложение ожидает ввода с клавиатуры и затем завершает свою работу.
После ввода строки с клавиатуры и записи ее в файл через поток наше приложение создает входной буферизованный поток, как это показано ниже:
InStream = new DataInputStream(
new BufferedInputStream(
new FileInputStream("output.txt")));
Далее для этого потока создается разборщик, который оформлен в отдельном классе TokenizerOfStream, определенном в нашем приложении:
TokenizerOfStream tos = new TokenizerOfStream();
Вслед за этим мы вызываем метод TokenizeIt, определенный в классе TokenizerOfStream, передавая ему в качестве параметра ссылку на входной поток:
tos.TokenizeIt(InStream);
Метод TokenizeIt выполняет разбор входного потока, отображая результаты разбора на консоли. После выполнения разбора входной поток закрывается методом close:
InStream.close();
Самое интересное в нашем приложении связано, очевидно, с классом TokenizerOfStream, поэтому перейдем к его описанию.
В этом классе определен только один метод TokenizeIt:
public void TokenizeIt(InputStream is)
{
. . .
}
Получая в качестве параметра ссылку на входной поток, он прежде всего создает для него разборщик класса StreamTokenizer:
StreamTokenizer stok;
stok = new StreamTokenizer(is);
Настройка параметров разборщика очень проста и сводится к вызовам всего двух методов:
stok.slashSlashComments(true);
stok.ordinaryChar('.');
Метод slashSlashComments включает режим распознавания комментариев в стиле языка программирования С++, а метод ordinaryChar объявляет символ ‘.’ обычным символом.
После настройки запускается цикл разбора входного потока, причем условием завершения цикла является достижение конца этого потока:
while(stok.nextToken() != StreamTokenizer.TT_EOF)
{
. . .
}
В цикле анализируется содержимое поля ttype, которое зависит от типа элемента, обнаруженного во входном потоке:
switch(stok.ttype)
{
case StreamTokenizer.TT_WORD:
{
str = new String("\nTT_WORD >" + stok.sval);
break;
}
case StreamTokenizer.TT_NUMBER:
{
str = "\nTT_NUMBER >" + Double.toString(stok.nval);
Внутри функции main мы определили несколько переменных.
Массив bKbdInput хранит строку, введенную с клавиатуры.
Массив buf используется для хранения строк (команд), которые сервер получает от клиентских приложений.
В переменной s класса DatagramSocket хранится ссылка на датаграмный сокет, который будет использован для приема команд от клиентских приложений.
Переменная pinp класса DatagramPacket хранит пакеты, полученные сервером из сети.
Переменные SrcAddress (класса InetAddress) и SrcPort типа int хранят, соответственно, адрес и порт узла, отправившего пакет.
Первое, что делает сервер - это создание датаграммного сокета:
s = new DatagramSocket(9998);
Конструктору передается номер порта 9998, на котором сервер будет принимать пакеты данных от клиентских приложений.
После создания сокета сервер создает объекта класса DatagramPacket, в который будут записываться приходящие от клиентов пакеты:
pinp = new DatagramPacket(buf, 512);
Конструктор пакета получает ссылку на массив buf, в который нужно будет записывать приходящие по сети данные, и размер этого массива, равный 512 байт.
Далее сервер запускает цикл приема пакетов и отображения их содержимого.
Пакет принимается простым вызовом метода receive из класса DatagramSocket:
s.receive(pinp);
Этот метод блокирует работу вызвавшей его задачи до тех пор, пока по данному сокету не будет получен пакет данных.
Когда пакет будет получен, наше приложение определяет адрес и порт отправителя:
SrcAddress = pinp.getAddress();
SrcPort = pinp.getPort();
Эта информация может пригодиться, если вы будете посылать отправителю ответный пакет.
Наше приложение ответные пакеты не посылает. Оно преобразует принятые данные в текстовую староку класса String, добавляет к этой строке номер порта отправителя и отображает эту информацию на консоли:
System.out.println("> " + str + " < " + "port: " +
SrcPort);
Цикл приема команд завершается, если от клиента пришла строка “quit”:
if(str.equals("quit"))
break;
Перед тем как завершить свою работу, наше приложение закрывает датаграммный сокет, вызывая для этого метод close:
s.close();
Внутри метода main определен массив bKbdInput, предназначенный для хранения данных, введенных с клавиатуры, переменная length, в которой хранится размер этих данных, рабочая строка str класса String, датаграммный сокет s и пакет pout класса DatagramPacket.
Прежде всего приложение определяет адрес узла, на котором оно выполняется, вызывая метод getLocalHost:
InetAddress OutAddress = InetAddress.getLocalHost();
Этот адрес будет использован для формирования передаваемого пакета данных.
Затем клиент создает датаграммный сокет, применяя для этого конструктор без параметров:
s = new DatagramSocket();
Напомним, что в этом случае для сокета выделяется любой свободный порт.
На следующем шаге приложение формирует передаваемый пакет, вызывая конструктор класса DatagramPacket:
pout = new DatagramPacket(bKbdInput, bKbdInput.length,
OutAddress, 9998);
Этому конструктору указывается адрес массива, содержащего введенные с клавиатуры данные, размер этого массива, адрес локального узла, на который нужно передать пакет, и номер порта серверного приложения.
Теперь все готово для запуска цикла передачи команд от клиента к серверу.
В этом цикле выполняется чтение строки с клавиатуры, причем размер прочитанной строки сохраняется в переменной length:
length = System.in.read(bKbdInput);
Далее, если строка не состоит из одного лишь символа перехода на новую строку, она отображается на косоли и посылается серверу методом send:
s.send(pout);
После того как пользователь введет строку “quit”, цикл завершается. Вслед за этим приложение закрывает датаграммный сокет:
s.close();
Описание исходного текста приложения CallCGI
Внутри метода main мы определили несколько переменных.
Массив bKbdInput предназначен для хранения строки, введенной с помощью клавиатуры. В переменную length записывается длина этой строки.
Строка str класса String используется в качестве рабочей.
Переменная u класса URL предназначена для хранения ссылки на объект URL, созданный для загрузочного файла программы CGI.
Ссылка на канал связи с программой CGI хранится в переменной с именем c класса URLConnection.
Переменные ps класса PrintStream и is класса DataInputStream хранят ссылки, соответственно, на выходной и входной потоки, через которые наше приложение обменивается данными с программой CGI.
После вывода приглашения на консоль наша программа вводит строку, которая будет передана программе CGI:
length = System.in.read(bKbdInput);
Далее массив bKbdInput преобразуется в строку str и перекодируется в кодировку URL. Эта кодировка была описана нами в 29 томе “Библиотеки системного программиста”. Она выполняется с помощью статического метода encode, определенного в классе URLEncoder:
String StrEncoded = URLEncoder.encode(str);
Перекодированная строка отображается на консоли:
System.out.println("Encoded string: >" + StrEncoded + "<");
На следующем этапе наше приложение создает объект класса URL для загрузочного файла программы CGI:
u = new URL("http://frolov/frolov-cgi/controls.exe");
Здесь предполагается, что программа CGI находится в файле controls.exe, который записан в виртуальный каталог frolov-cgi на сервере Web с адресом http://frolov). Про создание и настройку виртуальных каталогов для размещения расширений сервера Web мы рассказали в 29 томе “Библиотеки системного программиста”.
После создания объекта класса URL мы создаем канал с программой CGI как объект класса URLConnection:
c = u.openConnection();
Пользуясь этим каналом, мы вначале получаем выходной поток методом getOutputStream, а затем на его базе создаем форматированный выходной поток класса PrintStream, удобный для записи в него текстовых строк:
ps = new PrintStream(c.getOutputStream());
Через канал ps наше приложение передает программе CGI строку StrEncoded, а затем закрывает выходной поток, как это показано ниже:
ps.println(StrEncoded);
ps.close();
В этот момент на сервере Web уже запущена программа CGI, и она приняла переданные ей данные. Обработав эти данные, программа CGI записала в стандартный выходной поток динамически созданный ей документ HTML.
Для получения документа наше приложение CallCGI создала входной форматированный поток данных:
is = new DataInputStream(c.getInputStream());
Входной поток создан в два приема. Вначале с помощью метода getInputStream приложение создала обычный входной поток, а затем, на его базе, форматированный входной поток класса DataInputStream.
Получение от программы CGI динамически сформированного ей документа HTML наше приложение выполняет в цикле по строкам.
Строка документа HTML читается из входного форматированного потока методом readLine и записывается в переменную str:
str = is.readLine();
Если в процессе чтения был достигнут конец потока, цикл прерывается:
if(str == null)
break;
Строка, полученная методом readLine, отображается на консоли пиложения:
System.out.println(str);
После завершения цикла входной поток закрывается методом close:
is.close();
Описание исходного текста серверного приложения SocketServ
В методе main, получающем управление сразу после запуска приложения, мы определили несколько переменных.
Массив bKbdInput размером 256 байт предназначен для хранения строк, введенных при помощи клавиатуры.
В переменную ss класса ServerSocket будет записана ссылка на объект, предназначенный для установления канала связи через потоковый сокет (но не ссылка на сам сокет):
ServerSocket ss;
Ссылка на сокет, с использованием которого будет происходить передача данных, хранится в переменной с именем s класса Socket:
Socket s;
Кроме того, мы определили переменные is и os, соответственно, классов InputStream и OutputStream:
InputStream is;
OutputStream os;
В эти переменные будут записаны ссылки на входной и выходной поток данных, которые связаны с сокетом.
После отображения на консоли строки названия приложения, метод main создает объект класса ServerSocket, указывая конструктору номер порта 9999:
ss = new ServerSocket(9999);
Конструктор возвращает ссылку на объект, с использованием которого можно установить канал передачи данных с клиентом.
Канал устанавливается методом accept:
s = ss.accept();
Этот метод переводит приложение в состояние ожидания до тех пор, пока не будет установлен канал передачи данных.
Метод accept в случае успешного создания канала передачи данных возвращает ссылку на сокет, с применением которого нужно принимать и передавать данные.
На следующем этапе сервер создает входной и выходной потоки, вызывая для этого методы getInputStream и getOutputStream, соответственно:
is = s.getInputStream();
os = s.getOutputStream();
Далее приложение подготавливает буфер buf для приема данных и определяет переменную length, в которую будет записываться размер принятого блока данных:
byte buf[] = new byte[512];
int lenght;
Теперь все готово для запуска цикла приема и обработки строк от клиентского приложения.
Для чтения строки мы вызываем метод read применительно ко входному потоку:
lenght = is.read(buf);
Этот метод возвращает управление только после того, как все данные будут прочитаны, блокируя приложение на время своей работы. Если такая блокировка нежелательна, вам следует выполнять обмен данными через сокет в отдельной задаче.
Метод read возвращает размер принятого блока данных или -1, если поток исчерпан. Мы воспользовались этим обстоятельством для завершения цикла приема данных:
if(lenght == -1)
break;
После завершения приема блока данных мы преобразуем массив в текстовую строку str класса String, удаляя из нее символ перевода строки, и отображаем результат на консоли сервера:
System.out.println("> " + str);
Затем полученная строка отправляется обратно клиентскому приложению, для чего вызывается метод write:
os.write(buf, 0, lenght);
Методу write передается ссылка на массив, смещение начала данных в этом массиве, равное нулю, и размер принятого блока данных.
Для исключения задержек в передаче данных из-за накопления данных в буфере (при использовании буферизованных потоков) необходимо принудительно сбрасывать содержимое буфреа метдом flush:
os.flush();
И хотя в нашем случае мы не пользуемся буферизованными потоками, мы включили вызов этого метода для примера.
Теперь о завершающих действиях после прерывания цикла получения, отображения и передачи строк.
Наше приложение явням образом закрывает входной и выходной потоки данных, сокет, а также объект класса ServerSocket, с использованием которого был создан канал передачи данных:
is.close();
os.close();
s.close();
ss.close();
Для того чтобы аплет стал
Для того чтобы аплет стал мультизадачным, его класс, который наследуется от класса Applet, дополнительно реализует интерфейс Runnable, как это показано ниже:
public class MultiTask extends Applet implements Runnable
{
. . .
}
Внутри класса определяется поле с именем m_MultiTask типа Thread, которое предназначено для хранения ссылки на объект класса Thread, то есть на задачу:
Thread m_MultiTask = null;
Поле инициализируется значением null. Реальная ссылка на задачу будет сюда записана только после создания задачи.
Рассмотрим теперь методы класса.
Для создания задачи аплет Rectangles реализует интерфейс Runnable, то есть использует второй из описанных нами методов, как и предыдущий аплет.
Ниже мы рассмотрим наиболее важные методы аплета Rectangles.
Для выполнения плавного сдвига строк мы в нашем приложении создаем задачу, которая периодически рисует новые строки в нижней части окна, сдвигая перд этим старые строки вверх.
Основной класс аплета реализует интерфейс Runnable, поэтому для задачи рисования и сдвига строк текста мы не создаем свой класс на базе класса Thread.
Аплет создает задачу отображения символов текстовой строки, для чего он реализует интерфейс Runnable (как и предыдущий аплет). Эта задача рисует отдельные символы текстовой строки, каждый раз определяя их ширину.
Рассмотрим поля и самые важные методы, определенные в классах приложения Synchro.
Мы рассмотрим только самые важные методы нашего аплета - init и paint.
Для работы с базой данных мы создали класс SimpleDBMS, определив в нем конструктор, методы для добавления записей, извлечения записей по их порядковому номеру, а также метод для закрытия базы данных.
Сразу после запуска приложение создает кобъект класса InetAddress для локального узла, вызывая для этого статический метод getLocalHost:
iaLocal = InetAddress.getLocalHost();
Далее для созданного объекта вызывается метод getHostName, возвращающий строку имени локального узла:
System.out.println("Local host name: " +
iaLocal.getHostName());
Это имя отображается на консоли приложения.
Затем приложение определяет адрес IP локального узла, вызывая метод getAddress:
iaLocalIP = iaLocal.getAddress();
Напомним, что этот метод возвращает массив четырех байт адреса IP.
Адрес IP мы отображаем на консоли с помощью метода println:
System.out.println("Local host IP address: " +
(0xff & (int)iaLocalIP[0]) + "." +
(0xff & (int)iaLocalIP[1]) + "." +
(0xff & (int)iaLocalIP[2]) + "." +
(0xff & (int)iaLocalIP[3]));
Заметьте, что байты адреса записваются в массив типа byte как знаковые величины. Для того чтобы отображить их на консоли в виде положительных чисел, мы вначале выполняем явное преобразование к типу int, а затем обнуляем старший байт (так как такое преобразование выполняется с сохранением знака).
Наше приложение демонстрирует также другой способ получения адреса IP для объекта класса InetAddress, который заключается в вызове метода toString:
System.out.println("Local host IP address: " +
iaLocal.toString());
На втором этапе приложение InetAddressDemo вводит строку имени удаленного узла и, после удаления символа перехода на новую строку, пытается создать для введенного имени массив объектов класса InetAddress. Для этого приложение вызывает метод getAllByName:
iaRemoteAll = InetAddress.getAllByName(str);
Содержимое созданного массива отображается в цикле, причем адрес IP извлекается из объектов класса InetAddress методом toString:
for(int i = 0; i < iaRemoteAll.length; i++)
{
System.out.println("Remote host IP address: " +
iaRemoteAll[i].toString());
}
В главном классе нашего аплета определено два поля и несколько методов. Рассмотрим эти поля и самые важные методы.
Опишем наиболее важные методы приложения ImageDrawWait.
Наше приложение переопределяет метод imageUpdate, в котором отслеживает процесс загрузки фонового изображения.
Опишем основные методы, определенные в приложении DrawImageObserver.
Рассмотрим наиболее важные методы нашего приложения.
Обращаем еще раз ваше внимание на то, что в исходном тексте аплета Inspector импортируется описание класса Audio:
import Audio;
Это необходимо для того чтобы аплет мог получить доступ к полю auClip, определенному в этом аплете.
Рассмотрим поля и самые важные методы класса Inspector.
Так как ранее мы уже рассказывали довально подробно о классах приложения Combi, остановимся только на основных моментах.
Так как определение класса CombiFrame расположено в отдельном файле, мы должны импортировать описание этого класса в исходных текстах класса Combi:
import CombiFrame;
Определение адреса IP
Метод getAddress возвращает массив из чеырех байт адреса IP объекта. Байт с нулевым индексом этого массива содержит старший байт адреса IP.
Метод toString возвращает текстовую строку, которая содержит имя узла, разделитель ‘/’ и адрес IP в виде четырех десятичных чисел, разделенных точками.
Определение атрибутов файлов и каталогов
После того как вы создали объект класса File, нетрудно определить атрибуты этого объекта, воспользовавшись соответствующими методами класса File.
Определение длины файла в байтах
Длину файла в байтах можно определить с помощью метода length:
public long length();
Определение имени узла
С помощью метода getHostName вы можете определить имя узла, для которого был создан объект класса InetAddress.
Определение пути к файлу или каталогу
Метод getPath позволяет определить машинно-независимый путь файла или каталога:
public String getPath();
Определение родительского каталога
Если вам нужно определить родительский каталог для объекта класса File, то это можно сделать методом getParent:
public String getParent();
Определение типа объекта - файл или каталог
С помощью методов isDirectory и isFile вы можете проверить, чему соответствует созданный объект класса File - каталогу или файлу:
public boolean isDirectory();
public boolean isFile();
Определение типа указанного пути - абсолютный или относительный
С помощью метода isAbsolute вы можете определить, соответствует ли данный объект класса File файлу или каталогу, заданному абсолютным (полным) путем, либо относительным путем:
public boolean isAbsolute();
Определение времени последней модификации файла или каталога
Для определения времени последней модификации файла или каталога вы можете вызвать метод lastModified:
public long lastModified();
Заметим, однако, что этот метод возвращает время в относительных единицах с момента запуска системы, поэтому его удобно использовать только для относительных сравнений.
Ожидание извещения
Если вам нужно организовать взаимодействие задач таким образом, чтобы одна задача управляла работой другой или других задач, вы можете воспользоваться методами wait, notify и notifyAll, определенными в классе Object.
Метод wait может использоваться либо с параметром, либо без параметра. Этот метод переводит задачу в состояние ожидания, в котором она будет находиться до тех пор, пока для задачи не будет вызван извещающий метод notify, notifyAll, или пока не истечет период времени, указанный в параметре метода wait.
Как пользоваться методами wait, notify и notifyAll?
Метод, который будет переводиться в состояние ожидания, должен быть синхронизированным, то есть его следует описать как synchronized:
public synchronized void run()
{
while (true)
{
. . .
try
{
Thread.wait();
}
catch (InterruptedException e)
{
}
}
}
В этом примере внутри метода run определен цикл, вызывающий метод wait без параметров. Каждый раз при очередном проходе цикла метод run переводится в состояние ожидания до тех пор, пока другая задача не выполнит извещение с помощью метода notify.
Ниже мы привели пример задачи, вызывающией метод notify:
public void run()
{
while (true)
{
try
{
Thread.sleep(30);
}
catch (InterruptedException e)
{
}
synchronized(STask)
{
STask.notify();
}
}
}
Эта задача реализована в рамках отдельного класса, конструктору которого передается ссылка на задачу, вызывающую метод wait. Эта ссылка хранится в поле STask.
Обратите внимание, что хотя сам метод run не синхронизированный, вызов метода notify выполняется в синхронизированном режиме. В качестве объекта синхронизации выступает задача, для которой вызывается метод notify.
Ожидание загрузки добавленных изображений
Для того чтобы убедиться, что все изображения загружены, вы можете воспользоваться методом waitForAll. Этот метод инициирует загрузку изображений, а также задержит выполнение вызвавшей его задачи до момента полной загрузки всех изображений, добавленных в объект класса MediaTracker:
try
{
mt.waitForAll();
}
catch (InterruptedException ex)
{
}
Обратите внимание, что метод waitForAll может создавать исключение InterruptedException. Это исключение возникает, если по какой-либо причине процесс ожидания прерывается.
Чаще всего рисование выполняется в отдельной задаче, поэтому метод waitForAll должен вызываться в начале соответствующего метода run. Ниже мы привели исходные тексты приложения ImageDrawWait, в котором такое ожидание выполняется в методе paint, что приводит, однако, к блокировке работы аплета до момента загрузки всех изображений. В данном случае это не критично, так как кроме рисования изображений наш аплет ничего не делает, однако более предпочтительным является выполнение длительных процессов в отдельной задаче.
Ожидание загрузки изображений
Как мы уже говорили, загрузка изображений из сети Internet - длительный процесс, который в среднем идет со скоростью 1 Кбайт в секунду. Поэтому изображения загружаются навигатором в отдельной задаче. При этом метод getImage только создает объект класса Image, а метод drawImage инициирует загрузку изображения и рисует его. Причем если файл изображения имеет большую длину, он будет появляться в окне аплета постепенно по мере загрузки.
Однако в некоторых случаях было бы удобно вначале загрузить изображение полностью, а лишь затем выполнять рисование, чтобы изображение появилось на экране сразу. Кроме того, аплет может рисовать сразу несколько изображений в разных местах своего окна или показывать их по очереди в одном и том же месте для достижения эффекта анимации.
Есть ли способ определить, когда изображение будет загружено полностью?
Есть, и причем целых два. Один из них связан с использованием класса MediaTracker, специально предназначенного для этой цели и достаточно удобного в использовании, другой основан на переопределении одного из методов интерфейса ImageObserver.
Ожидание завершения задачи
С помощью метода join вы можете выполнять ожидание завершения работы задачи, для которой этот метод вызван.
Существует три определения метода join:
public final void join();
public final void join(long millis);
public final void join(long millis, int nanos);
Первый из них выполняет ожидание без ограничения во времени, для второго ожидание будет прервано принудительно через millis миллисекунд, а для третьего - через millis миллисекунд и nanos наносекунд. Учтите, что реально вы не сможете указывать время с точностью до наносекунд, так как дискретность системного таймера компьютера намного больше.
Передача данных между клиентом и сервером
После того как серверное и клиентское приложения создали потоки для приема и передачи данных, оба этих приложения могут читать и писать в канал данных, вызывая методы read и write, определенные в классах InputStream и OutputStream.
Ниже мы представили фрагмент кода, в котором приложение вначале читает данные из входного потока в буфер buf, а затем записывает прочитанные данные в выходной поток:
byte buf[] = new byte[512];
int lenght;
lenght = is.read(buf);
os.write(buf, 0, lenght);
os.flush();
На базе потоков класса InputStream и OutputStream вы можете создать буферизованные потоки и потоки для передачи форматированных данных, о которых мы рассказывали раньше.
Передача данных с использованием сокетов
В библиотеке классов Java есть очень удобное средство, с помощью которых можно организовать взаимодействие между приложениями Java и аплетами, работающими как на одном и том же, так и на разных узлах сети TCP/IP. Это средство, родившееся в мире операционной системы UNIX, - так называемые сокеты (sockets).
Что такое сокеты?
Вы можете представить себе сокеты в виде двух розеток, в которые включен кабель, предназначенный для передачи данных через сеть. Переходя к компьютерной терминологии, скажем, что сокеты - это программный интерфейс, предназначенный для передачи данных между приложениями.
Прежде чем приложение сможет выполнять передачу аили прием данных, оно должно создать сокет, указав при этом адрес узла IP, номер порта, через который будут передаваться данные, и тип сокета.
С адресом узла IP вы уже сталкивались. Номер порта служит для идентификации приложения. Заметим, что существуют так называемые “хорошо известные” (well known) номера портов, зарезервированные для различных приложений. Например, порт с номером 80 зарезервирован для использования серверами Web при обмене данными через протокол HTTP.
Что же касается типов сокетов, то их два - потоковые и датаграммные.
С помощью потоковых сокетов вы можете создавать каналы передачи данных между двумя приложениями Java в виде потоков, которые мы уже рассматривали во второй главе. Потоки могут быть входными или выходными, обычными или форматированными, с использованием или без использования буферизации. Скоро вы убедитесь, что организовать обмен данными между приложениями Java с использованием потоковых сокетов не труднее, чем работать через потоки с обычными файлами.
Заметим, что потоковые сокеты позволяют передавать данные только между двумя приложениями, так как они предполагают создание канала между этими приложениями. Однако иногда нужно обеспечить взаимодействие нескольких клиентских приложений с одним серверным. В этом случае вы можете либо создавать в серверном приложении отдельные задачи и отдельные каналы для каждого клиентского приложения, либо воспользоваться датаграммными сокетами. Последние позволяют передавать данные сразу всем узлам сети, хотя такая возможность редко используется и часто блокируется администраторами сети.
Для передачи данных через датаграммные сокеты вам не нужно создавать канал - данные посылаются непосредственно тому приложению, для которого они предназначены с использованием адреса этого приложения в виде сокета и номера порта. При этом одно клиентское приложение может обмениваться данными с несколькими серверными приложениями или наоборот, одно серверное приложение - с несколькими клиентскими.
К сожалению, датаграммные сокеты не гарантируют доставку передаваемых пакетов данных. Даже если пакеты данных, передаваемые через такие сокеты, дошли до адресата, не гарантируется, что они будут получены в той же самой последовательности, в которой были переданы. Потоковые сокеты, напротив, гарантируют доставку пакетов данных, причем в правильной последовательности.
Причина отстутствия гарантии доставки данных при использовании датаграммных сокетов заключается в использовании такими сокетами протокола UDP, который, в свою очередь, основан на протоколе с негарантированной доставкой IP. Потоковые сокеты работают через протокол гарантированной доставки TCP.
В 23 томе “Библиотеки системного программиста”, который называется “Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets” мы уже рассказывали про сокеты в среде операционной системы Microsoft Windows. В этой книге вы найдете примеры приложений, составленных на языке программирования С и работающих как с потоковыми, так и с датаграммными сокетами.
Переименование файлов и каталогов
Для переименования файла или каталога вы должны создать два объекта класса File, один из которых соответствует старому имени, а второй - новому. Затем для перовго из этих объектов нужно вызвать метод renameTo, указав ему в качестве параметра ссылку на второй объект:
public boolean renameTo(File dest);
В случае успеха метод возвращает значение true, при возникновении ошибки - false. Может также возникать исключение SecurityException.
Поля класса Audio
В поле m_ClipName хранится имя звукового файла, которое передается через параметр ClipName из документа HTML. По умолчанию для этого параметра используется значение kaas.au.
Строка PARAM_ClipName хранит имя указанного выше параметра.
Ссылка на интерфейс AudioClip хранится в поле auClip:
AudioClip auClip;
Следующие три поля хранят ссылки на кнопки, предназначенные для управления проигрыванием звукового файла:
Button btPlay;
Button btLoop;
Button btStop;
Поле fLoopPlay типа boolean используется для флага, которым отмечается режим проигрывания звукового файла в цикле.
Поля класса Combi
В классе Combi определено поле с именем m_fStandAlone:
boolean m_fStandAlone = false;
Если приложение работает автономно, в это поле записывается значение true, если как аплет в составе документа HTML - false. По умолчанию это поле инициализируется значением false, однако если приложение запускается автономно, метод main записывает в него значение true:
applet_Combi.m_fStandAlone = true;
Поля класса DrawRectangles
Класс DrawRectangles определен для задачи рисования прямоугольников. В поле g класа хранится контекст отображения окна аплета, а в поле dimAppWndDimension - размеры этого окна. Значения этих полей определяются конструктором класса по ссылке на главный класс аплета.
В поле g класса Graphics хранится контекст отображения окна аплета, определенный конструктором класса DrawRectangles.
Поле dimAppWndDimension хранит размеры окна аплета.
Поля класса HorzScroll
В поле m_HorzScroll хранится сслыка на задачу рсиования строки.
В документе HTML нашему аплету передается несколько параметров, задающих отображаемую строку и определяющих ее внешний вид.
В поле m_Str хранится отображаемая строка.
Шритф, стилевое оформление и размер смиволов строки хранится, соответственно, в полях m_Fnt, m_style и m_size. Цвет символов строки одинаковый и хранится в поле m_color.
Поле m_delay хранит текущее время задержки между отображением отдельных символов строки в миллисекундах.
Поля класса ImageDraw
В классе ImageDraw определено два поля:
Image FloppyDiskImg;
Image CDDiskImg;
В первом из них хранится ссылка на изображение флоппи-диска, во втором - ссылка на изображение компакт-диска.
Поля класса Inspector
В поле appContext хранится ссылка на интерфейс AppletContext, с помощью которого мы будем получать список аплетов.
Этот список будет записан в поле eApplets класса Enumeration.
Когда в процессе поиска наш аплет найдет аплет Audio, то в поле appAudio будет записана ссылка на него.
Кроме того, в классе Inspector определены поля btPlay, btLoop и btStop для хранения ссылок на кнопки управления аплетом Audio.
В классе MultiTask2 мы определили
В классе MultiTask2 мы определили два поля с именами m_DrawRectThread и m_DrawEllipseThread:
DrawRectangles m_DrawRectThread = null;
DrawEllipse m_DrawEllipseThread = null;
Эти поля являются ссылками на классы, соответственно DrawRectangles и DrawEllipse. Первый из них создан для рисования прямоугольников, а второй - эллипсов.
Указанные поля инициализируются занчением null, что соответствует неработающим или несозданным задачам.
Поля класса NotifyTask
В классе NotifyTask мы определили одно поле STask класса Thread, которое хранит ссылку на задачу, работой которой управляет данный класс. Конструктор класса NotifyTask записывает сюда ссылку на задачу рисования прямоугольников.
Поля класса Scroller
В поле m_Scroller записывается ссылка на задачу рисования и сдвига строк текста.
Шесть полей типа String с именами от m_String1 до m_String6 предназначены для хранения сдвигаемых строк текста.
Поля с именами от PARAM_String1 до PARAM_String6 хранят имена параметров аплета.
Поля класса ShowChart
В классе ShowChart определены три поля.
Поле SrcURL класса URL хранит адрес URL файла исходных данных для круговой диаграммы. В поле URLContent типа Object будет переписано содержимое этого файла. И, наконец, в поле errno хранится текущий код ошибки, если она возникла, или нулевое значение, если все операции были выполнены без ошибок.
Поля класса SimpleDBMS
Поля idx dat являются объектами класса RandomAccessFile и представляют собой, соответственно, ссылки на файл индекса и файл данных. Поле idxFilePointer типа long используется как рабочее и хранит текущее смещение в файле.
Поля основного класса аплета
В основном классе аплета определены две ссылки m_DrawRectThread и m_NotifyTaskThread:
DrawRectangles m_DrawRectThread = null;
NotifyTask m_NotifyTaskThread = null;
Первая из них является ссылкой на объект класса DrawRectangles, определенный нами для задачи рисования прямоугольников. Вторая переменная представляет собой ссылку на объект класса NotifyTask, который создан для задачи, управляющей работой задачи рисования.
Получение абсолютного пути к каталогу
Метод getAbsolutePath возвращает абсолютный путь к файлу или каталогу, который может быть машинно-зависимым:
public String getAbsolutePath();
Получение имени файла или каталога
Метод getName возвращает имя файла или каталога для заданного объекта класса File (имя выделяется из пути):
public String getName();
Получение информации о параметрах аплета
Если аплет получает из документа HTML параметры, желательно (но вовсе не обязательно) определить метод getParameterInfo. Этот метод возвращает массив строк описаний параметров, в котором находятся имена и типы параметров, а также строки описаний параметров.
Для примера мы привели фрагмент исходного текста приложения TEXTOUT, описанного нами в 30 томе, посвященном разработке приложений Java:
public String[][] getParameterInfo()
{
String[][] info =
{
{ PARAM_Str1, "String", "Text string to write" },
{ PARAM_Str2, "String", "Text string to write" },
{ PARAM_Str3, "String", "Text string to write" },
. . .
{ PARAM_Font1, "String", "Text font" },
{ PARAM_Font2, "String", "Text font" },
{ PARAM_Font3, "String", "Text font" },
. . .
{ PARAM_Type1, "String", "Font type" },
{ PARAM_Type2, "String", "Font type" },
{ PARAM_Type3, "String", "Font type" },
};
return info;
}
Вызывая метод getParameterInfo для найденного аплета, аплет-инспектор может многое узнать о его параметрах. Эти знания нужны, в частности, для получения значений параметров.
Получение контекста аплетов
Для получения контекста аплетов, или ссылки на интерфейс AppletContext вы должны воспользоваться методом getAppletContext, определенным в классе Applet:
AppletContext appContext;
appContext = getAppletContext();
Далее, вызывая методы, определенные в интерфейсе AppletContext, вы можете получить ссылку на конкретный аплет или список ссылок на все аплеты.
Получение списка содержимого каталога
С помощью метода list вы можете получить список содержимого каталога, соответствующего данному объекту класса File. В классе File предусмотрено два варианта этого метода - без параметра и с параметром:
public String[] list();
public String[] list(FilenameFilter filter);
Первый из этих методв возвращает массив строк с именами содержимого каталога, не включая текущий каталог и родительский каталог. Второй позволяет получить список не всех объектов, хранящихся в каталоге, а только тех, что удовлетворяют условиям, определенным в фильтре filter класса FilenameFilter.
Пример приложения, которое просматривает содержимое каталога и использует при этом фильтр, вы найдете ниже в разделе “Приложение DirList”.
Получение списка всех аплетов
С помощью метода getApplet вы сможете получить ссылку на один аплет, заданный своим именем, но наша задача - получить ссылки на все аплеты, расположенные в текущем документе HTML. Это можно сделать с помощью метода getApplets, определенного следующим образом:
public abstract Enumeration getApplets();
Перед вызовом этого метода вы должны определить список класса Enumeration, например, следующим образом:
Enumeration eApplets;
eApplets = appContext.getApplets();
Получение ссылки на аплет
Метод getApplet возвращает ссылку на аплет, заданный своим именем:
public abstract Applet getApplet(String name);
Это имя должно быть указано в параметре NAME оператора <APPLET> языка HTML, или оно должно передаваться аплету через параметр с именем NAME.
Первый из этих способов демонстрируется ниже:
<applet
code=MyApplet.class
id=MyApplet
width=320
height=100
name="TestApplet">
</applet>
Здесь параметр NAME задает для аплета MyApplet имя TestApplet.
Второй способ был использован нами в следующем фрагменте документа HTML:
<applet
code=MyApplet.class
id=MyApplet
width=320
height=100 >
<param name=TestApplet value="TestApplet">
</applet>
Если аплет с указанным именем имеется в текущем документе HTML, метод getApplet возвращает ссылку на соответствующий объект. Пользуясь этой ссылкой, можно получить доступ к полям и методам, определенным в аплете как public. Если же аплет не найден, метод getApplet возвращает значение null.
Получение строки информации об аплете
Обратите внимание, что в каждом нашем аплете мы определяли метод getAppletInfo:
public String getAppletInfo()
{
return "Name: Inspector\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
Этот метод возвращает строку информации об аплете и, вообще говоря, необязателен. Однако если ваш аплет будет взаимодействовать с другими аплетами, то он может оказать существенную помощь в распознавании аплета.
В самом деле, просматривая в цикле список аплетов, расположенных в текущем документе HTML, аплет-инспектор может вызывать для каждого найденного аплета метод getAppletInfo с целью получения строки, идентифицирующей данный аплет:
appName = currentApplet.getAppletInfo();
Если же аплет не обеспечил метод getAppletInfo, то будет вызван одноименный метод из базового класса Applet, который просто возвращает значение null. Очевидно, это значение нельзя использовать для идентификации аплета.