Учебник в формате DOC - Кафедра анализа данных и

advertisement
КАЗАНСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
ФАКУЛЬТЕТ ВЫЧИСЛИТЕЛЬНОЙ МАТЕМАТИКИ И КИБЕРНЕТИКИ
КАФЕДРА ЭКОНОМИЧЕСКОЙ КИБЕРНЕТИКИ
ПИНЯГИНА О.В.,
КАШИНА О.А.,
АНДРИАНОВА А.А.
Практикум
по программированию
на языке JAVA
Казань – 2007
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
УДК 004.43
ББК 32.973.26 – 018.1
Печатается по постановлению редакционно-издательского совета
факультета вычислительной математики и кибернетики
Казанского государственного университета
Рецензенты:
………………………………..
………………………………….
Пинягина О.В., Кашина О.А., Андрианова А.А.
Практикум по программированию на языке Java.: Учебное пособие/ О.В. Пинягина, О.А. Кашина, А.А. Андрианова – Казань: Казанский государственный университет, 2007. – 141 с.
В данном учебном пособии рассматриваются основы языка Java, создание Java-апплетов и Java-приложений, сервлетов и Java Server Pages.
Пособие разработано для поддержки компьютерных занятий по курсу
“Учебная практика. Java” для студентов, обучающихся по специальности
“061800 – Математические методы в экономике” и ориентировано на читателя, знакомого с языком C++, а, следовательно, имеющего представление об
объектно-ориентированном программировании. В качестве среды программирования используется JDK 1.4 (или более поздняя версия) и редактор
JCreator.
Web-ресурс по данному курсу находится на сайте кафедры экономической кибернетики по адресу: http://kek.ksu.ru/EOS/JAVA/default.html.
 Казанский государственный
университет, 2007
 Пинягина О.В., Кашина О.А.,
Андрианова А.А., 2007
2
Практикум по программированию на языке Java
Содержание
Содержание .............................................................................................3
Предисловие ...........................................................................................5
Что нужно для программирования на Java .....................................7
Глава 1. Основы языка Java ...............................................................8
СОЗДАНИЕ ПЕРВОГО АППЛЕТА ............................................................................................................... 8
СИНТАКСИС ЯЗЫКА. ТИПЫ ДАННЫХ.
КОНСТАНТЫ И
ПЕРЕМЕННЫЕ. МАССИВЫ. ...................................................................................................................... 11
КЛАССЫ ДЛЯ ТИПОВ ДАННЫХ И СТРУКТУР ДАННЫХ .............................................................. 16
ОПЕРАЦИИ JAVA........................................................................................................................................... 18
ОПЕРАТОРЫ JAVA ........................................................................................................................................ 19
Глава 2. Объектно-ориентированные принципы Java ...............21
ИНКАПСУЛЯЦИЯ ........................................................................................................................................... 21
НАСЛЕДОВАНИЕ ........................................................................................................................................... 24
АБСТРАКТНЫЕ КЛАССЫ И ИНТЕРФЕЙСЫ ........................................................................................ 25
ПОЛИМОРФИЗМ ............................................................................................................................................. 27
ВНУТРЕННИЕ КЛАССЫ............................................................................................................................... 30
Глава 3. Разработка апплетов ...........................................................32
РАБОТА С ГРАФИКОЙ.................................................................................................................................. 32
РАБОТА СО ШРИФТАМИ ............................................................................................................................ 33
ПЕРЕДАЧА ВНЕШНИХ ДАННЫХ В АППЛЕТ. .................................................................................... 35
AWT И ОБРАБОТКА СОБЫТИЙ .............................................................................................................. 39
ОБРАБОТКА СОБЫТИЙ МЫШИ ............................................................................................................... 47
ОБРАБОТКА СОБЫТИЙ КЛАВИАТУРЫ ................................................................................................ 51
РАБОТА С ПОТОКАМИ ................................................................................................................................ 52
Глава 4. Разработка приложений .....................................................61
СОЗДАНИЕ ПЕРВОГО ПРИЛОЖЕНИЯ ................................................................................................... 61
РАБОТА С ИСКЛЮЧЕНИЯМИ ................................................................................................................... 62
РАБОТА С ФАЙЛАМИ................................................................................................................................... 65
СЕРИАЛИЗАЦИЯ ............................................................................................................................................ 72
РАБОТА С БАЗАМИ ДАННЫХ ................................................................................................................... 74
СОЗДАНИЕ ОКОННЫХ ПРИЛОЖЕНИЙ................................................................................................. 79
СЕТЕВЫЕ ПРИЛОЖЕНИЯ ........................................................................................................................... 84
Глава 5. Немного о сервлетах и Java Server Pages .......................93
ЧТО НУЖНО ДЛЯ РАЗРАБОТКИ СЕРВЛЕТОВ И JSP? .................................................................... 93
ПРИМЕР СЕРВЛЕТА ...................................................................................................................................... 94
ПРИМЕР JSP .................................................................................................................................................... 96
3
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
ПЕРЕДАЧА ФАЙЛА ИЗ HTML-ФОРМЫ СЕРВЛЕТУ ...................................................................... 98
АППЛЕТ КАК ИНТЕРФЕЙС К СЕРВЛЕТУ .......................................................................................... 100
ИСПОЛЬЗОВАНИЕ СЕРВЛЕТОВ ДЛЯ ГЕНЕРАЦИИ GIF-ИЗОБРАЖЕНИЙ ........................... 105
Практические задания ......................................................................108
ЗАДАНИЕ 1 ..................................................................................................................................................... 108
ЗАДАНИЕ 2 ..................................................................................................................................................... 109
ЗАДАНИЕ 3 ..................................................................................................................................................... 110
ЗАДАНИЕ 4 ..................................................................................................................................................... 110
ЗАДАНИЕ 5. .................................................................................................................................................... 111
ЗАДАНИЕ 6 ..................................................................................................................................................... 112
ЗАДАНИЕ 7 ..................................................................................................................................................... 113
Приложение 1. ЧАсто задаваемые ВОпросы ...............................114
Приложение 2. Как работать с Jcreator ........................................119
Приложение 3. Введение в настраиваемые типы данных ........122
Приложение 4. Работа с JDK в режиме командной строки ......125
Приложение 5. Коды примеров ......................................................128
Литература ..........................................................................................141
4
Практикум по программированию на языке Java
Предисловие
Язык Java – одно из самых новых и модных средств программирования
нашего времени. Он начинал разрабатываться как часть проекта создания передового программного обеспечения для различных бытовых приборов.
Можно сказать, что он произошел от языка C++, сохранив 80% его мощности и лишь 20% сложности (первая версия языка была создана в 1995 г. и
называлась Oak). Это сокращение сложности привело, как ни странно, к тому, что Java получился более "объектно-ориентированным", чем его славный
предок.
Но, разумеется, новый язык был разработан не просто для того, чтобы
программисты тратили меньше времени и сил на его изучение. Java имеет
собственные характерные черты.
Во-первых, в отличие от других языков программирования, в которых
программы компилируются для конкретных типов процессоров, Java создает
машинно-независимый промежуточный код, называемый "кодом виртуальной машины". И только при выполнении апплета или запуске автономной
программы вызывается так называемая "виртуальная машина Java", которая переводит этот код в двоичный код конкретного процессора. Таким образом, написанная на Java программа будет работать под большинством операционных систем. Заманчиво, не правда ли? Но за любое преимущество
приходится чем-то расплачиваться. В данном случае – тем, что у виртуальной машины очень низкая скорость работы. Программы, написанные, к примеру, на языке C и скомпилированные под определенную платформу, выполняются в 10-20 раз быстрее, чем на Java. Закон сохранения: выигрываем в
универсальности – проигрываем в скорости (хотя в настоящее время разработаны компиляторы, которые сразу преобразуют исходный код программы
в код определенной операционной системы).
5
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Во-вторых, на языке Java можно писать не только автономные программы, но и специальные модули для размещения на web-страницах. Эти
модули называются апплетами, а средства их просмотра встроены в большинство браузеров.
В-третьих, язык Java оказался также удобным для разработки webприложений на серверной стороне – это так называемые сервлеты и Java
Server Pages. Созданы несколько web-серверов на платформе Java.
Что же касается первоначальной идеи создания языка Java – разработки программ для бытовой техники – то она в настоящее время выражается,
главным образом, в технологии создания «умных домов», а также в разработке игр и прочих приложений для сотовых телефонов и других устройств.
Первая глава учебника рассматривает основные конструкции языка и
их применение. Вторая глава посвящена объектно-ориентированным принципам Java. В третьей и четвертой главах рассматриваются разнообразные
вопросы, касающиеся построения апплетов и приложений. В пятой главе изложены основы создания сервлетов и Java Server Pages.
6
Практикум по программированию на языке Java
Что нужно для программирования на Java
Для работы с Java необходимы следующие программные средства:

JDK,

графическая оболочка,

справочная система.
Базовая часть средств разработки программ на Java представлена в
программном комплексе JDK (Java Developer Kit) от компании Sun. Не путайте JDK с JRE (Java Runtime Environment). Второй является составной частью первого и служит только для запуска Java-программ, но никак не для
их создания.
Учтите, что без JDK вам никак не обойтись. Даже если вы установите
какие-либо редакторы для Java, за их красивым графическим интерфейсом
зачастую будет скрываться все тот же JDK.
Установите дистрибутив JDK (он называется как-нибудь вроде
j2sdk1_4_0-win.exe) в какой-либо каталог, например, в C:\ j2sdk1.4.0. Теперь
можно приступать к написанию программ на Java.
Хотя Java-апплеты и Java-приложения можно запускать в режиме командной строки, разумеется, удобнее использовать какой-либо редактор с
графическим интерфейсом, например, JCreator.
При изучении любого нового языка или среды программирования каждый уважающий себя программист использует справочную систему с описанием всех возможностей данного языка. В онлайн-режиме (при наличии доступа в Интернет) удобно применять справочную систему ресурса
http://java.sun.com.
7
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Глава 1. Основы языка Java
Создание первого апплета
Апплет – это специальный модуль для размещения на web-странице.
Средства просмотра апплетов встроены в большинство браузеров.
Создадим апплет типа "Hello, world!". Для создания апплета выполним
следующие действия. Сначала создадим исходный текст апплета. Запустим
редактор JCreator и наберем в нем текст
// Простой пример апплета
import java.awt.*;
import java.applet.*;
public class FirstApplet extends Applet
{
String s="Изучаем Java!!!";
public void paint(Graphics g)
{
Font f =new Font("Times New Roman", Font.BOLD, 24);
g.setFont(f);
g.drawString(s, 5, 25);
}
}
В первых строках подключаются стандартные пакеты Java, содержащие классы для работы с графикой и апплетами.
Стандартные классы Java скомпонованы в так называемые пакеты,
хранящиеся в архивных файлах с расширением .jar. Система пакетов похожа
на систему каталогов и подкаталогов (если архив раскрыть, то как раз и получится некоторая иерархия каталогов и файлов). Таким образом, команда
import java.awt.*; (не забывайте точку с запятой!) представляет собой инструкцию: "подключить все классы, содержащиеся в подкаталоге awt корневого каталога классов java".
8
Практикум по программированию на языке Java
Любой апплет, который вы создаете, представляет собой производный
класс от стандартного класса Applet. В нашем апплете переопределяется
только один метод – paint (перерисовка окна). В качестве параметра он получает графический контекст окна.
Метод paint вызывается автоматически каждый раз, когда апплет нуждается в перерисовке. Для принудительной перерисовки следует вызывать
метод repaint без параметров, который сам вызовет метод paint.
Текст в окно будем выводить крупным шрифтом размером 24, поэтому
сначала создадим объект класса Font. Обратите внимание, что в одном и том
же операторе объявляется переменная font типа Font и создается сам объект
с помощью операции new. Для этого вызывается конструктор объекта с тремя параметрами. В качестве второго аргумента передается значение статической переменной BOLD класса Font. Выберем созданный шрифт в контекст
окна и напечатаем строку "Изучаем Java!".
Любой файл c исходным текстом апплета на Java должен иметь расширение .java. Кроме того, он должен иметь имя, совпадающее с именем
класса. Поэтому нам придется сохранить файл под именем FirstApplet.java.
Включите файл в проект и откомпилируйте. Если в файле были синтаксические ошибки, будут выданы соответствующие сообщения. Если ошибок не было, будет создан файл FirstApplet.class.
Для того чтобы запустить апплет на выполнение, нужно создать
HTML-страницу следующего вида:
<HTML>
<HEAD><TITLE>Applet title</TITLE></HEAD>
<BODY>
<APPLET CODE=FirstApplet WIDTH=300 HEIGHT=300></APPLET>
</BODY>
</HTML>
Как видим, тег <applet> служит для включения апплета в текст страницы, его
главный атрибут code содержит имя файла с расширением class (расширение
9
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
указывать не обязательно), в котором находится откомпилированный код апплета. Если апплет находится не в том же каталоге, что HTML-файл, можно
задать явный путь к апплету с помощью атрибута codebase, например,
<applet code=FirstApplet codebase="http://kek.ksu.ru/java">
</applet>
HTML-страницу с апплетом также можно просмотреть через браузер. Ваш
первый апплет создан.
Как правило, в производных от Applet классах, кроме метода paint, переопределяется также метод init. Этот метод служит для инициализации апплета и является "точкой входа" в программу – именно этот метод вызывается первым при выполнении апплета (конструкторы для апплетов обычно не
используют). Кроме того, иногда переопределяют методы start и stop
(например, при работе с потоками). Метод destroy вызывается при выгрузке
апплета.
Приведем пример использования метода init.
import java.awt.*;
import java.applet.*;
public class FirstApplet extends Applet
{
String s;
public void init()
{
s="Изучаем Java!!!";
}
public void paint(Graphics g)
{
Font f =new Font("Times New Roman", Font.BOLD, 24);
g.setFont(f);
g.drawString(s, 5, 25);
}
}
Задание для самостоятельной работы
1. Изучите более подробно стандартные классы Applet и Graphics. Какие методы они имеют?
10
Практикум по программированию на языке Java
2. От каких базовых классов класс Applet является производным? Какие методы они имеют?
Синтаксис языка. Типы данных.
Константы и переменные. Массивы.
Синтаксис языка
Правила написания программ на Java очень похожи на правила С.
Вспомним основные моменты:
1. В программах различаются прописные и строчные буквы. Так, слова
WORD, WoRd, word, worD означают разные идентификаторы. IF может быть именем переменной, а if – ключевое слово (см. Операторы
Java).
2. Кстати, об идентификаторах. Идентификатор – слово (последовательность символов без пробелов и знаков препинания), состоящее из букв,
цифр, знаков _ и $, начинающееся с буквы.
3. Каждый оператор заканчивается точкой с запятой, кроме составного.
4. Составной оператор (блок) обрамляется фигурными скобками.
5. Комментарии могут занимать несколько строк, тогда они заключаются
в символы /* */. Комментарий до конца строки можно создать символами //.
Типы данных
В языке Java имеются следующие стандартные типы данных:
ТИП
КЛЮЧЕВОЕ
СЛОВО
РАЗМЕР
ДИАПАЗОН
КОНСТАНТЫ
11
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
логический
boolean
целочисленные
byte
8-битное целое
-128: 127
-128, 127
short
16-битное целое
-32 768: 32 767
-32 768
32 767
int
32-битное целое
-2 147 483 648:
2 147 483 647
-2 147 483 648
2 147 483 647
long
64-битное целое
-9 223 372 036 854 775 808:
1, 127, 32768, …
9 223 372 036 854 775 807
символьный
char
8-битные символы
16-битные знаки
UNICODE
вещественные
float
32-битное вещественное
-3.4*10-38: 3.4*1038
1.1F
double
64-битное вещественное
-1.7*10-308: 1.7*10308
1.2, 1.2D, 12E-1
пустое
значение
true, false
'a', '\n', '\x1b'
void
Обратите внимание, что long и short – это не модификаторы, как в С, а
самостоятельные типы данных. Кроме того, существует специфическая константа null для присвоения переменной какого-либо класса "пустой ссылки".
Можно неявно преобразовывать значения разных типов по принципу
"от меньшего к большему" (например, значение byte можно присвоить переменной типа char, char - int, int - long).
Для явного преобразования типа используется синтаксис:
int x; long y;
...
x = (int) y;
В том случае, когда целое значение превышает диапазон для данного
типа, возникнет переполнение, и в переменную будет записано совсем не то
число, которое мы предполагали (см. Задание 1). А в том случае, когда точ12
Практикум по программированию на языке Java
ность (количество знаков после запятой) превышает точность вещественного
типа, возникнет ошибка округления (см. Задание 2).
Любая переменная должна быть предварительно объявлена. Объявление переменной выглядит так:
тип переменная1, переменная2,...;
Например,
int x, Count, Sum;
При объявлении переменной можно сразу присвоить ей значение.
int x=1234, Count=1, Sum=0;
Заметим, что глобальных переменных в Java нет, все переменные локальные в пределах функции или класса. Исключением можно считать статические члены класса, к которым можно обратиться из любого места.
Массивы
В языке Java, как и в C, можно использовать массивы. Объявляются
они следующим образом:
int [] list;
или
int list [];
Можно объявлять многомерные массивы:
int [][] matrix;
Перед использованием массив должен быть либо инициализирован,
либо для него явно должна быть выделена память. Инициализировать массив
можно так же, как и в C:
int [] list = {9,8,7,6,5,4,3,2,1,0};
int [][] matrix = {{3,2,1},{6,5,4},{9,8,7}};
13
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Память выделяется с помощью операции new:
int [] list = new int [10];
int [][] matrix = new int [5][5];
В качестве размерности массива можно использовать переменные.
int m=4, n=3;
int [][] matrix = new int [m][n];
Массивы в Java имеют некоторые свойства объектов. Так, для получения размерности массива можно обратиться к его свойству length:
int [] list = new int [10];
int n=list.length;
В случае использования двумерного массива также возможно получение той или иной размерности с помощью свойства length. Как и в языке
С, имя двумерного массива matrix рассматривается здесь как массив массивов. Поэтому свойство length будет содержать число строк двумерного массива. Количество же элементов в i-ой строке двумерного массива (кстати,
количество элементов в разных строках двумерного массива может быть различным) можно будет получить с помощью свойства length массива
matrix[i].
int [][] matrix={{1,2,3,4}, {5,6,7}, {9,0}};
System.out.println("length matrix="+matrix.length);
for(int k=0; k<matrix.length; k++)
System.out.println
("length of matrix["+k+"]="+ matrix[k]. length);
Метод System.out.println печатает заданное выражение в стандартный
поток вывода (подробнее о потоках ввода и вывода – в параграфе “Работа с
файлами”).
При создании массива объектов с помощью операции new конструкторы объектов, в отличие от языка C++, не вызываются. Создается только массив ссылок на объекты. Впоследствии можно будет создать объекты с помо-
14
Практикум по программированию на языке Java
щью операции new или присвоить элементам массива ссылки на другие объекты.
Пусть имеется следующий класс с именем Myclass.
class Myclass
{
int i,j;
public Myclass(int x, int y)
{
i=x;
j=y;
}
public void show()
{
System.out.println("i="+i+"; j="+j+"\n");
}
}
Использование массива объектов этого класса может быть, например,
таким:
Myclass ob=new Myclass(3,4);
Myclass [] mas=new Myclass[5];
System.out.println("length of array="+mas.length);
for(int k=0; k<mas.length; k++)
{
// инициализируется k-ый элемент массива
mas[k]=ob;
mas[k].show();
}
Задания для самостоятельной работы
1.
Создайте апплет с примером переполнения целых чисел. Под-
сказка: присвойте переменной целого типа результат арифметической операции, превышающий диапазон данного типа.
2.
Создайте апплет с примером потери точности для вещественных
чисел. Подсказка: присвойте переменной число с большим количеством знаков после запятой.
15
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Классы для типов данных и структур данных
Один из основных принципов Java, как и вообще объектноориентированного программирования, гласит: "Всё является объектом" –
"Everything is an object".
Поэтому специальные классы-оболочки разработаны даже для стандартных типов данных. Они размещены в пакете java.lang. Пакет java.lang
всегда подключается по умолчанию (его не нужно импортировать) и содержит наиболее часто используемые классы. Разработаны классы:
 Boolean
 Integer
 Byte
 Long
 Character
 Short
 Double
 String
 Float
 Void
Они содержат, в основном, методы для преобразования данных из одного типа в другой. В частности, объекты и методы этих классов мы будем
использовать при передаче параметров в апплет.
Обратите внимание, что для сравнения объектов одного и того же
класса нельзя использовать знак '='. В этом случае сравниваются адреса объектов. Для сравнения содержимого объектов в большинстве классов переопределяется метод equals.
В данном пакете содержится также класс String. Этот класс несколько
отличается от других классов Java:
 объекты класса String не обязательно создавать командой new. Допустима инициализация объекта константой:
String s="Изучаем Java";
или присвоение константы или другой строки:
String s1, s2;
s1="Изучаем Java";
s2=s1;
16
Практикум по программированию на языке Java
 для объектов класса String переопределена операция '+', которая используется для конкатенации (сцепления) строк. Сцеплять можно не только
строки, но и строку с числом, строку с булевской переменной и т.п. Если
нужно сцепить строку с объектом, то для этого объекта должна быть переопределена функция toString, которая должна возвращать представление
объекта в качестве строки. Например,
class Dog
{
public String Name;
public String Breed;
public String toString()
{
return ("Собака, имя: "+Name+",порода: "+Breed);
}
// ...
}
 для строк реализована возможность разбиения на слова с помощью
объекта класса StringTokenizer, который находится в пакете java.util.
Простой цикл разбиения строки на слова выглядит так:
String s="Изучаем язык Java! Очень интересно";
// создаем объект StringTokenizer. В конструктор передаем
// строку для разбиения и строку символов-разделителей
StringTokenizer T=new StringTokenizer(s," ");
// пока будет найден следующий символ-разделитель
while(T.hasMoreTokens())
{
// выводим фрагмент строки до найденного разделителя
System.out.println(T.nextToken());
}
И, наконец, в пакете java.util есть несколько интересных классов для
более сложных структур данных:
 Date – дата;
 Vector – вектор;
 Stack – стэк;
 Hashtable – хэш-таблица.
17
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Задание для самостоятельной работы
Рассмотрите самостоятельно пакеты java.lang и java.util.
Операции Java
В языке Java, как и в любом другом, имеются различные операции: логические, арифметические, сравнения и т.п. Их удобно сгруппировать в таблицу с учетом приоритетов.
Операция
Название
Формат
.
Разделитель
Объект.Элемент
[]
Индекс
Массив[Индекс]
()
Вызов функции
Функция (Параметры)
++
Постфиксный инкремент
Переменная++
++
Префиксный инкремент
++Переменная
--
Постфиксный декремент
Переменная--
--
Префиксный декремент
--Переменная
~
Поразрядная инверсия
~Выражение
!
Логическое отрицание
!Выражение
instanceof
Проверка принадлежности
объекта классу
Объект instanceof Класс
new
Выделение памяти
new тип
*
Умножение
Выражение * Выражение
/
Деление
Выражение / Выражение
%
Остаток от целочисленного деВыражение % Выражение
ления
+
Сложение
18
Выражение + Выражение
Практикум по программированию на языке Java
-
Вычитание
Выражение – Выражение
<<
Сдвиг влево
Выражение << Выражение
>>
Сдвиг вправо
Выражение >> Выражение
>>>
Сдвиг вправо с заполнением
нулями
Выражение >>> Выражение
<
Меньше, чем
Выражение < Выражение
>
Больше, чем
Выражение > Выражение
<=
Меньше или равно
Выражение <= Выражение
>=
Больше или равно
Выражение >= Выражение
==
Сравнение на равенство
Выражение = = Выражение
!=
Сравнение на неравенство
Выражение != Выражение
&
Побитовое "И"
Выражение & Выражение
^
Побитовое исключающее
"ИЛИ"
Выражение ^ Выражение
|
Побитовое "ИЛИ"
Выражение | Выражение
&&
Логическое "И"
Выражение && Выражение
||
Логическое "ИЛИ"
Выражение || Выражение
?:
Условная операция
ЛогическоеВыражение ? ВыражениеЕслиИстина : ВыражениеЕслиЛожь
Операция=
Присваивание
Переменная*=Выражение
(вместо * может быть любая бинарная
арифметическая или побитовая операция)
Задание для самостоятельной работы
Протестируйте в вашем апплете все вышеуказанные операции.
Операторы Java
19
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
В Java используются те же операторы, что и в С++: if-else, while, dowhile, for, switch-case, break, continue, return.
Впрочем, можно сделать некоторые дополнительные замечания.
Во-первых, вспомним, что в языке Java имеется специальный логический тип boolean. Именно этот логический тип и используется там, где требуется логическое выражение. В языке C, как вы помните, в этих случаях
применяется целый тип. Так, например, для организации бесконечного цикла
в C можно использовать оператор
while (1)
{
}
В Java это выражение недопустимо, вместо него нужно использовать
while (true)
{
}
Во-вторых, в языке Java нет оператора goto.
20
Практикум по программированию на языке Java
Глава 2. Объектно-ориентированные принципы Java
Инкапсуляция
Инкапсуляция является одной из основных характеристик объектноориентированного языка программирования. Суть инкапсуляции состоит в
том, что в рамках класса объединяются переменные и функции и получившийся объект рассматривается как единое целое.
Кроме того, существует возможность назначить права доступа к объектам класса и/или составным частям объектов. В языке Java существуют следующие модификаторы прав доступа:
public
общий доступ, для любых объектов и извне
private
частный доступ, только из методов данного класса
private protected
защищенный доступ, только из методов данного класса и его
производных классов
protected
защищенный доступ, из методов данного класса и его производных классов, а также из классов, определенных в том же
пакете (каталоге)
По умолчанию переменные, методы и классы имеют тип protected.
Обращаться к переменным и методам объекта следует с помощью операции "точка", т.е.:
имяОбъекта.имяПеременной
имяОбъекта.имяМетода(параметры)
Если переменная или метод определены с ключевым словом static, это
означает, что данная переменная или метод относится ко всему классу в целом, а не к объекту. Вызываются такие переменные и методы через имя класса:
имяКласса.имяПеременной
имяКласса.имяМетода(параметры)
21
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Если переменная определена с ключевым словом final, это означает,
что данная переменная содержит неизменяемое значение, т.е., является константой. Константы можно инициализировать как при компиляции:
final String s = "Изучаем Java";
так и в ходе выполнения:
final Date today = Date();
Если метод определен с ключевым словом final, это означает, что его
нельзя будет переопределить в производном классе.
В рамках одного класса могут присутствовать несколько методов с одним и тем же названием, отличающихся друг от друга только количеством
и/или типом параметров. Нельзя определить два метода с одинаковыми
названиями, отличающиеся только типом возвращаемого значения.
Метод, название которого совпадает с именем класса, называется конструктором и вызывается при создании объекта командой new. Конструкторов с разными списками параметров может быть несколько.
Если ни один конструктор не определен, компилятор создает конструктор по умолчанию – без параметров.
Если явно определен хотя бы один конструктор, то конструктор по
умолчанию не создается.
Деструкторов, т.е., методов, автоматически вызываемых при удалении
объекта, в языке Java нет.
Для примера создадим класс "Собака", содержащий переменные и методы, в том числе два конструктора:
class Dog
{
public String Name;
public String Breed;
public int AverageWeight;
public int AverageHeight;
public void ShowDog(Graphics g, int x, int y)
{
g.drawString("Имя:"+Name, x,y);
g.drawString("Порода: "+Breed, x,y+20);
22
Практикум по программированию на языке Java
if (AverageWeight!=-1)
g.drawString("Вес:"+AverageWeight, x,y+40);
if (AverageHeight!=-1)
g.drawString("Рост:"+AverageHeight, x,y+60);
}
public Dog(String name, String breed,
int weight, int height)
{
Name=name;
Breed=breed;
AverageWeight=weight;
AverageHeight=height;
}
public Dog(String name)
{
Name=name;
Breed="Неизвестна";
AverageWeight=-1;
AverageHeight=-1;
}
}
В том случае, когда переменные имеют тип доступа private, к ним
нельзя обращаться из других классов. Для таких переменных обычно определяют так называемые интерфейсные функции (set- и get-функции). Пусть,
например, в определении класса "Собака" все переменные будут закрытые.
Определим для них следующие методы:
class Dog
{
private
private
private
private
String Name;
String Breed;
int AverageWeight;
int AverageHeight;
public void setName(String Name)
{
this.Name=Name;
}
public String getName()
{
return Name;
}
public void setBreed(String Breed)
{
this.Breed=Breed;
}
public String getBreed()
{
return Breed;
}
public void setAverageWeight(int AverageWeight)
{
this.AverageWeight=AverageWeight;
}
23
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
public int getAverageWeight()
{
return AverageWeight;
}
public void setAverageHeight(int AverageHeight)
{
this.AverageHeight=AverageHeight;
}
public int getAverageHeight()
{
return AverageHeight;
...
}
}
Заметьте, что в Java, как и в С, используется ключевое слово this для
явной ссылки на текущий объект.
Наследование
Не менее важной характеристикой объектно-ориентированного языка
программирования является наследование. Суть наследования состоит в создании производного класса, который имеет возможности базового класса
плюс свои собственные возможности. Говорят, что производный класс расширяет (extends) базовый класс. Любой производный класс может расширять только один базовый класс. О модификаторах доступа – см. "Инкапсуляция".
Пусть базовый класс "Сотрудник" выглядит следующим образом:
class Employee
{
public Employee(String Name,
String Position, double Salary)
{
this.Name=Name;
this.Position=Position;
this.Salary=Salary;
}
public String ShowEmp()
{
return ("Служащий: " + Name +
" Должность: " + Position +
" Зарплата: " + Salary);
}
private String Name;
private String Position;
private double Salary;
}
24
Практикум по программированию на языке Java
Заметим, что в конструкторе возникает конфликт имен, поэтому для
явного обращения к переменным объекта используется ключевое слово this.
Определим производный класс "Менеджер". Для вызова конструктора
базового класса в производном классе используется ключевое слово super.
class Manager extends Employee
{
public Manager(String Name,
String Position, double Salary,
String Car, double Bonus, int StockOptions)
{
super(Name, Position, Salary);
this.Car=Car;
this.Bonus=Bonus;
this.StockOptions=StockOptions;
}
public String ShowManager()
{
return ("Автомобиль: " + Car +
" Надбавка: "+ Bonus +
" Количество акций: "+ StockOptions);
}
private String Car;
private double Bonus;
private int StockOptions;
}
В производном классе функции базового класса могут быть переопределены. При использовании переопределенных функций в производных
классах может возникнуть необходимость явно вызвать функцию базового
класса. Для этого используется выражение:
super.имяФункции(параметры);
Абстрактные классы и интерфейсы
Абстрактным называется класс, который содержит хотя бы один абстрактный метод. Абстрактный метод содержит только описание (заголовок
с параметрами), но не содержит тела метода. И в определении класса, и в
описании метода нужно указывать ключевое слово abstract.
Определим абстрактный класс "Фигура".
25
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
abstract class Figure
{
public int x, y, width, height;
public Figure(int x, int y, int width, int height)
{
this.x=x;
this.y=y;
this.width=width;
this.height=height;
}
abstract double getArea();
abstract double getPerimeter();
}
Объекты абстрактного класса создавать нельзя. Сначала нужно определить производный класс, который расширяет абстрактный класс и предоставляет определения для его абстрактных методов. Определим производный
класс "Круг" от класса "Фигура":
class Circle extends Figure
{
public double r;
public Circle(int x, int y, int width)
{
super(x, y, width, width);
r=(double)width / 2.0;
}
public double getArea()
{
return (r * r * Math.PI);
}
public double getPerimeter()
{
return (2 * Math.Pi * r);
}
}
Интерфейс в языке Java представляет собой "чисто абстрактный
класс", т.е. класс, все методы которого являются абстрактными. Производный от интерфейса класс "раскрывает" (implements) интерфейс, предоставляя коды для всех его методов. Класс может расширять только один базовый класс, но раскрывать он может несколько интерфейсов.
26
Практикум по программированию на языке Java
Переменные интерфейса могут быть только static (т.е., переменные
класса, а не объекта) и final (т.е., константы).
Для примера вместо абстрактного класса "Форма" определим интерфейс:
interface Shape
{
abstract double getArea();
abstract double getPerimeter();
}
Производный от него класс "Круг" будет выглядеть следующим образом:
class Circle implements Shape
{
public int x, y;
public double r;
public Circle(int x, int y, int r)
{
this.x=x;
this.y=y;
this.r=r;
}
public double getArea()
{
return (r * r * Math.PI);
}
public double getPerimeter()
{
return (2 * Math.Pi * r);
}
}
Как видим, производный класс наследует только поведение, так как
интерфейс не может содержать свойства (т.е., переменные) объекта.
Полиморфизм
Полиморфизм представляет собой характеристику, которая присуща
только настоящим объектно-ориентированным языкам. Суть полиморфизма
состоит в том, что тип объекта, к которому мы обращаемся, иногда может
27
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
определяться только на этапе выполнения (это называют также поздним,
или динамическим связыванием).
Продолжим рассмотрение примера с геометрическими фигурами из
прошлой темы. В нашем примере одна и та же переменная типа Figure (это
абстрактный класс!) в зависимости от сгенерированной случайной величины
может ссылаться как на объект производного класса Circle, так и на объект
производного класса Square. В базовом классе заданы основные переменные
и абстрактные методы – заготовки для будущего поведения объектов производных классов.
abstract class Figure
{
public int x, y, width, height;
public Figure(int x, int y, int width, int height)
{
this.x=x;
this.y=y;
this.width=width;
this.height=height;
}
abstract double getArea();
abstract double getPerimeter();
abstract void show(Graphics g);
}
Производные от базового класса Figure классы Circle и Square содержат свои дополнительные переменные и реализацию методов – собственно
поведение объектов.
28
Практикум по программированию на языке Java
class Circle extends Figure
{
public double r;
public Circle(int x, int y, int width)
{
super(x, y, width, width);
r=(double)width / 2.0;
}
public double getArea()
{
return (r * r * Math.PI);
}
public double getPerimeter()
{
return (2 * Math.PI * r);
}
public void show(Graphics g)
{
g.setColor(Color.yellow);
g.fillOval(x,y,(int)(2*r),(int)(2*r));
}
}
class Square extends Figure
{
public double side;
public Square(int x, int y, int width)
{
super(x, y, width, width);
side=width;
}
public double getArea()
{
return (side * side);
}
public double getPerimeter()
{
return (4 * side);
}
public void show(Graphics g)
{
g.setColor(Color.cyan);
g.fillRect(x,y,(int)side,(int)side);
}
}
И, наконец, для тестирования этих классов создан класс, производный
от Applet. Обратите внимание, что в функции перерисовки paint ничего не
29
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
говорится о том, какая именно фигура будет изображаться. Для рисования
фигуры вызывается метод show именно того объекта, который был создан в
функции init. Таким образом, тип объекта определяется только на этапе выполнения.
public class Poly extends Applet
{
Figure myFigure;
public void init()
{
switch ((int)(Math.random()*2))
{
case 0:
myFigure = new Circle((int)(Math.random()*100),
(int)(Math.random()*100),
10+(int)(Math.random()*100));
break;
case 1:
myFigure = new Square((int)(Math.random()*100),
(int)(Math.random()*100),
10+(int)(Math.random()*100));
break;
}
}
public void paint(Graphics g)
{
myFigure.show(g);
g.setColor(Color.black);
g.drawString("Площадь="+myFigure.getArea(),5, 25);
g.drawString("Периметр="+myFigure.getPerimeter(),
5, 45);
}
}
Внутренние классы
В Java существует возможность создавать внутренние классы. Казалось бы, зачем это нужно? Дело в том, что внутренние классы имеют доступ
к переменным внешнего класса. Тем самым упрощается процесс обмена информацией.
30
Практикум по программированию на языке Java
Следующий пример рассмотрен подробно в параграфе AWT и обработка событий.
Так, в этом примере взаимодействуют три объекта:
 класса, производного от Applet;
 класса Panel для элементов управления;
 класса, производного от Canvas для рисования.
Объект-канва должен иметь информацию о том, какие значения выбраны в элементах управления. Так как эта информация хранится в объектеапплете, канва должна иметь доступ к его переменным. Если канва является
объектом внутреннего класса по отношению к апплету, то она имеет к ним
доступ, и проблема решена.
31
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Глава 3. Разработка апплетов
Работа с графикой
Для вывода графической информации в окно апплета нужно переопределить метод paint класса Applet (точнее, одного из его родительских классов). Он выглядит следующим образом:
public void paint(Graphics g)
{
}
Этот метод вызывается в тех случаях, когда с точки зрения браузера
необходима перерисовка всего окна апплета или его части. Для принудительной перерисовки апплета программным образом вызывайте метод
repaint. Как видим, в качестве параметра метод paint получает графический
контекст.
Для рисования простых графических объектов класс Graphics имеет
методы:
drawLine(int x1, int y1, int x2, int y2)
рисование линии
drawRect(int x1, int y1, int x2, int y2)
рисование контура прямоугольника
fillRect(int x1, int y1, int x2, int y2)
рисование прямоугольника,
заполненного текущим цветом
fillPolygon(int x[], int y[], int count)
рисование многоугольника,
заполненного текущим цветом
и т.п.
Рассмотрим пример Графические примитивы.
import java.applet.*;
import java.awt.*;
public class Primitives extends Applet
{
32
Практикум по программированию на языке Java
public void paint(Graphics g)
{
int pointX[]={100, 180, 10, 190, 30};
int pointY[]={10, 180,30,70,190};
g.setColor(Color.red);
g.fillRoundRect(205,25,100,50,20,20);
g.setColor(Color.yellow);
g.fillRoundRect(225,45,100,50,20,20);
g.setColor(Color.green);
g.fillRoundRect(245,65,100,50,20,20);
g.setColor(Color.blue);
g.fillPolygon(pointX, pointY, pointX.length);
}
}
Для назначения текущего цвета используется метод setColor(Color)
класса Graphics.
Задание для самостоятельной работы
Рассмотрите более подробно класс Color.
Работа со шрифтами
Для работы со шрифтами используется класс Font. Конструктор класса
Font выглядит следующим образом:
Font(String название, int стиль, int размер)
Класс содержит несколько полезных статических переменных:
BOLD
жирное начертание
33
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
ITALIC
курсивное начертание
PLAIN
обычное начертание
Для примера создадим объект шрифт семейства TimesRoman размером
18 pt, жирного начертания:
Font font=new Font ("TimesRoman", Font.BOLD, 18);
Для того чтобы назначить созданный шрифт в качестве текущего, используется метод setFont класса Graphics:
public void paint(Graphics g)
{
g.setFont(font);
g.drawString(Mes, 10, 10);
}
Получить разнообразные характеристики шрифта можно с помощью
объектов класса FontMetrics. Создадим объект класса FontMetrics на основе
существующего шрифта:
public void paint(Graphics g)
{
FontMetrics fm;
Font BigFont=new Font("TimesRoman", Font.BOLD, 36);
g.setFont(BigFont);
fm=g.getFontMetrics();
// ....
}
Далее можно, например, получить размер строк или отдельных символов данного шрифта (в пикселах):
x = fm.charWidth('Щ');
// ширина буквы Щ
y = fm.stringWidth("Изучаем Java!");
// ширина строки "Изучаем Java!"
Задание для самостоятельной работы
Изучите более подробно методы классов Font и FontMetrics.
34
Практикум по программированию на языке Java
Передача внешних данных в апплет.
Вы уже знаете, что для размещения апплета на HTML-странице используется контейнер <applet></applet>. Кроме того, в апплет можно передать параметры, например:
<APPLET CODE=Star WIDTH=220 HEIGHT=220>
<PARAM name=x value='100'>
</APPLET>
Каждый параметр должен иметь имя – name (регистр в имени не учитывается) и значение – value.
В данном примере из HTML-документа в апплет Star.class могут передаваться разные значения параметра color:
<html><head></head>
<body bgcolor=Black bottommargin=0 leftmargin=0
topmargin=0 rightmargin=0>
<APPLET CODE=Star WIDTH=220 HEIGHT=220>
<PARAM name=color value='red on black'></APPLET>
</body></html>
Апплет получает параметры с помощью функции getParameter, обычно ее вызов размещают в функции инициализации:
import java.applet.*;
import java.awt.*;
35
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
public class Star extends Applet
{
String col;
public void init()
{
col=getParameter("color");
}
public void paint(Graphics g)
{
if (col.equals("black on red"))
{
g.setColor(Color.black);
setBackground(Color.red);
}
else if (col.equals("red on black"))
{
g.setColor(Color.red);
setBackground(Color.black);
}
else
{
g.setColor(Color.blue);
}
int pointX[]={100, 180, 10, 190, 30};
int pointY[]={10, 180, 30, 70, 190};
g.fillPolygon(pointX, pointY, pointX.length);
}
}
Запомните, что функция getParameter возвращает значение типа
String. Если нужно получить числовые значения, придется выполнить преобразование, как в следующем примере.
В следующем примере из HTML-страницы в апплет Star2.class пере-
36
Практикум по программированию на языке Java
даются 3 целых числа – интенсивность красной, зеленой и синей компонент
цвета (от 0 до 255)
<html><head></head>
<body bgcolor=Black bottommargin=0 leftmargin=0 topmargin=0
rightmargin=0>
<APPLET CODE=Star2 WIDTH=220 HEIGHT=220>
<PARAM name=red value=0>
<PARAM name=green value=200>
<PARAM name=blue value=150></APPLET>
</body></html>
Сам апплет выглядит следующим образом:
import java.applet.*;
import java.awt.*;
public class Star2 extends Applet
{
int r,g,b;
public void init()
{
r=Integer.parseInt(getParameter("red"));
g=Integer.parseInt(getParameter("green"));
b=Integer.parseInt(getParameter("blue"));
}
public void paint(Graphics gr)
{
gr.setColor(new Color(r,g,b));
int pointX[]={100, 180, 10, 190, 30};
int pointY[]={10, 180, 30, 70, 190};
gr.fillPolygon(pointX, pointY, pointX.length);
}
}
Обратите внимание, что строковые значения параметров с помощью
статической функции parseInt класса Integer преобразуются в целые числа.
Еще более громоздко выглядит преобразование параметра в число с
плавающей точкой. Предположим, мы имеем переменную xmin типа double
и параметр xMin. Преобразование параметра будет выглядеть так:
xmin=(Double.valueOf(getParameter("xMin"))).doubleValue();
Разберите это выражение самостоятельно.
37
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Существует другой способ передачи данных из HTML-страницы в апплет. Средствами языка клиентских сценариев JavaScript можно вызывать
любой public-метод апплета, который определен в HTML-документе. Тем
самым дается возможность перестройки, изменения состояния апплета уже
после загрузки его в браузер. Для этого требуется в тэге <applet> указать
значение атрибута name. После этого любой public-метод апплета вызывается с помощью следующей команды JavaScript:
document.имяАпплета.имяМетода(параметрыМетода)
Добавим в апплет Star2 метод, который позволяет изменять цвет нарисованной звезды.
public void UserColor(int Red, int Green, int Blue)
{
// запоминаем параметры метода в переменных класса
r=Red; g=Green; b=Blue;
// инициируем перерисовку изображения
repaint();
}
Для вызова метода уже загруженного апплета с именем app
<APPLET CODE=Star2 NAME=app WIDTH=220 HEIGHT=220>
<PARAM name=red value=0>
<PARAM name=green value=200>
<PARAM name=blue value=150>
</APPLET>
создадим следующие командные кнопки в HTML-документе с апплетом, при
нажатии на которые звезда будет рисоваться ярко-красным, зеленым и синим
цветами, соответственно:
<input type="button" value="Красный"
onclick="document.app.UserColor(255,0,0)">
<input type="button" value="Зеленый"
onclick="document.app.UserColor(0,255,0)">
38
Практикум по программированию на языке Java
<input type="button" value="Синий"
onclick="document.app.UserColor(0,0,255)">
Задание для самостоятельной работы
Измените цвет фона в апплете. Какому классу принадлежит эта функция?
AWT и обработка событий
В апплетах иногда бывает нужно разместить разнообразные управляющие элементы – текстовые поля, кнопки, списки и т.п. Для этого в языке
Java существует технология, которая называется Abstract Window Toolkit. В
этой технологии не используется привычный для нас принцип размещения
элементов "левый верхний угол – ширина – высота". Вместо этого применяются так называемые "менеджеры раскладок" (layout managers).
FlowLayout – потоковая раскладка – используется в апплетах по умолчанию и предполагает, что элементы размещаются последовательно слева
направо (точно так же, как в языке HTML). По умолчанию элементы центрируются и при изменении размеров апплета не изменяют свои размеры, а просто переупорядочиваются:
public class Flowlay extends Applet
{
public void init()
{
add(new Button("Кнопка1"));
add(new Button("Кнопка2"));
add(new Button("Кнопка3"));
add(new Button("Кнопка4")); }
}
GridLayout – табличная раскладка – используется в том случае, если
элементы удобно размещать в табличном порядке. Количество ячеек и строк
таблицы определяется при создании объекта GridLayout. По умолчанию
элемент заполняет ячейку таблицы полностью (можно задать размер отступа
элемента от границы ячейки) и изменяет свои размеры при изменении разме39
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
ров контейнера, в котором он содержится. По умолчанию ячейки таблицы
заполняются слева направо, сверху вниз.
public class Gridlay extends Applet
{
public void init()
{
setLayout(new GridLayout(2,2));
add(new Button("Кнопка1"));
add(new Button("Кнопка2"));
add(new Button("Кнопка3"));
add(new Button("Кнопка4")); }
}
BorderLayout – раскладка "по сторонам света" – использует структуру,
показанную на рисунке внизу. Если какая-либо область остается пустой, то
для нее место не выделяется. Элемент управления растягивается по размеру
области, также можно задать зазор между областями.
Север
З
а
п
а
д
Центр
В
о
с
т
о
к
Юг
public class Borderlay extends Applet
{
public void init()
{ setLayout(new BorderLayout(10,10));
add("North", new Button("Кнопка1"));
add("Center", new Button("Кнопка2"));
add("East", new Button("Кнопка3"));
add("West", new Button("Кнопка4"));
add("South", new Button("Кнопка5"));
}
}
40
Практикум по программированию на языке Java
Для создания управляющих элементов используется операция new, вызывающая соответствующий конструктор. После создания элемента его нужно включить в состав контейнера (в самом простом случае – в состав апплета) с помощью метода add.
Создание кнопки:
Button MyButton = new Button("OK");
// параметр - надпись на кнопке
add(MyButton);
Создание текстового поля:
TextField MyText=new TextField("100", 5);
// параметры - значение и размер
add(MyText);
Создание флажка:
Checkbox MyCheckbox=new с("название флажка", true);
// второй параметр - включен или выключен флажок
add(MyCheckbox);
Создание группы радиокнопок:
CheckboxGroup bkcolor= new CheckboxGroup();
Checkbox cb1= new Checkbox("белый фон", true, bkcolor);
Checkbox cb2= new Checkbox("голубой фон", false, bkcolor);
add(cb1);
add(cb2);
Создание списка:
Choice color=new Choice();
color.addItem("Черный");
color.addItem("Синий");
color.addItem("Красный");
color.addItem("Зеленый");
add(color);
Для того чтобы уметь обрабатывать события, поступающие от элементов управления, апплет должен раскрывать интерфейсы
 ActionListener (для текстовых полей и кнопок) и/или
 ItemListener (для списков, флажков и радиокнопок),
например,
41
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
public class Spir extends Applet
implements ActionListener, ItemListener
{
}
В функции init данного апплета нужно назначить слушателя для отслеживания событий элементов управления. Обычно в роли слушателя выступает сам апплет:
MyButton.addActionListener(this);
MyText.addActionListener(this);
MyCheckbox.addItemListener(this);
// Для списка назначается один слушатель
color.addItemListener(this);
// В наборе радиокнопок слушатель назначается для каждой кнопки
cb1.addItemListener(this);
cb2.addItemListener(this);
Класс, раскрывающий интерфейс ActionListener, должен предоставлять код для метода actionPerformed, например
public void actionPerformed(ActionEvent e)
{
if (e.getSource()==MyText)
System.out.println
("Произошло событие для текстового поля");
if (e.getSource()==MyButton)
System.out.println("Произошло событие для кнопки");
}
Класс, раскрывающий интерфейс ItemListener, должен предоставлять
код для метода itemStateChanged, например
public void itemStateChanged(ItemEvent e)
{
if (e.getItemSelectable()==color)
{
System.out.println((String)e.getItem());
}
if (e.getSource()==cb1)
System.out.println("Выбран белый фон");
if (e.getSource()==cb2)
System.out.println("Выбран голубой фон");
if (e.getSource()==MyCheckbox)
if(e.getStateChange()==1)
System.out.println("Флажок включен");
42
Практикум по программированию на языке Java
else
System.out.println("Флажок выключен");
}
Заметим, что флажок имеет два состояния – включен или выключен.
Так что при возникновении события с флажком должно анализироваться состояние флажка. Это позволяет делать метод getStateChange класса
ItemEvent, который возвращает 1, если флажок перешел в состояние
"выбран", и 2 в противном случае.
И, наконец, элементы управления можно размещать не на самом апплете, а предварительно создать панель для них и размещать элементы уже
на этой панели, например:
Panel p1=new Panel(); // панель для элементов управления
setLayout(new BorderLayout());
add("North",p1); // размещаем панель на апплете
p1.add(MyButton); // размещаем элементы на панели
p1.add(MyText);
p1.add(MyCheckbox);
p1.add(cb1);
p1.add(cb2);
p1.add(color);
В качестве примера рассмотрим подробно следующий апплет.
В заголовке апплета указано, что он раскрывает интерфейсы
ItemListener (для работы со списком) и ActionListener (для работы с текстовым полем) В апплете определены переменные:
43
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
private int n=20;
// количество пикселов в единице
private String name="";
// название функции
private Choice tip=new Choice();
// элемент - список
private TextField size;
// элемент - текстовое поле
Panel p1=new Panel();
// панель для элементов управления
MyCanvas p2=new MyCanvas();
// канва для рисования
Апплет разбит на 2 части: верхняя – панель для элементов управления
(объект класса Panel), нижняя – канва для рисования (объект пользовательского класса MyCanvas). Метод init апплета имеет вид:
public void init()
{
// назначаем менеджер раскладок для апплета
setLayout(new BorderLayout());
// назначаем менеджер раскладок для панели управления
p1.setLayout(new GridLayout(2,2));
// размещаем панель в северной части апплета
add("North",p1);
// размещаем канву в центральной части апплета
add("Center",p2);
// создаем метку и включаем ее в панель
p1.add(new Label("Выбирайте функцию: "));
// создаем элементы списка
tip.addItem("Синус");
tip.addItem("Косинус");
tip.addItem("Тангенс");
// назначаем слушателя для списка
tip.addItemListener(this);
// включаем список в панель
p1.add(tip);
// создаем метку и включаем ее в панель
p1.add(new Label("Пикселов в единичном отрезке:"));
// создаем текстовое поле
size=new TextField(""+n,5);
// включаем текстовое поле в панель
p1.add(size);
// назначаем слушателя для текстового поля
size.addActionListener(this);
}
Поскольку апплет раскрывает интерфейс ItemListener, он должен содержать функцию
public void itemStateChanged(ItemEvent e)
// параметр функции - событие
{
// из переменной-события получаем, какой объект его вызвал;
44
Практикум по программированию на языке Java
// эту проверку можно было не делать, поскольку у нас
// всего один объект мог вызвать данное событие
if (e.getItemSelectable()==tip)
{
// получаем выбранную строку из списка
name=(String)e.getItem();
// выполняем принудительную перерисовку канвы
p2.repaint();
}
}
Поскольку апплет раскрывает интерфейс ActionListener, он должен
содержать функцию
public void actionPerformed(ActionEvent e)
// параметр функции - событие
{
// из переменной-события получаем, какой объект его вызвал;
// эту проверку можно было не делать, поскольку у нас
// всего один объект мог вызвать данное событие
if (e.getSource()==size)
{
// получаем значение из текстового поля и
// преобразуем его в целое число
n=Integer.parseInt(size.getText());
// выполняем принудительную перерисовку канвы
p2.repaint();
}
}
И, наконец, определяем канву для рисования – класс MyCanvas как
внутренний класс апплета.
// Панель для рисования
class MyCanvas extends Canvas
{
int M, // ширина панели
N, // высота панели
x, y, xold, yold;
public void paint (Graphics g)
{
M=getSize().width;
N=getSize().height;
setBackground(new Color(200,200,200));
g.drawLine(M/2,0,M/2,N);
g.drawLine(0,N/2,M,N/2);
for (x=0; x<M; x++)
45
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
{
if (name.equals("Синус"))
{
y=(int)(N/2-Math.sin((double)(x-M/2)/n)*n);
}
else if (name.equals("Косинус"))
{
y=(int)(N/2-Math.cos((double)(x-M/2)/n)*n);
}
else if (name.equals("Тангенс"))
{
y=(int)(N/2-Math.tan((double)(x-M/2)/n)*n);
}
else return;
if (x==0) { xold=x; yold=y; }
g.drawLine(xold, yold, x, y);
xold=x; yold=y;
}
}
}
Почему здесь удобно использовать внутренний класс? Дело в том, что
канва при рисовании использует переменные из апплета: название функции и
масштаб числовых осей. В качестве внутреннего класса канва получает прямой доступ к этим переменным, хотя они имеют тип доступа private.
Можно ли обойтись без внутреннего класса? Можно, например, сделать класс MyCanvas внешним, перенести эти переменные в класс
MyCanvas
 и объявить их как protected. Апплет может получить к ним доступ,
поскольку эти два файла (основной класс и MyCanvas.class) находятся в одном каталоге. Тогда в функциях actionPerformed и itemStateChanged присвоение значений этим переменным будет выглядеть так:
p2.n=Integer.parseInt(size.getText());
p2.name=(String)e.getItem();
 или объявить их как private. Тогда для доступа к этим переменным
нужны будут интерфейсные функции (set- и get-), например:
public void setName (String name) { this.name=name; }
public void setN (int n) { this.n=n; }
46
Практикум по программированию на языке Java
а в функциях actionPerformed и itemStateChanged присвоение значений
этим переменным будет выглядеть так:
p2.setN(Integer.parseInt(size.getText()));
p2.setName((String)e.getItem());
Задание для самостоятельной работы
Изучите самостоятельно классы ItemEvent и ActionEvent, которые используются при передаче параметров в обработчики событий для управляющих элементов (в частности, выясните, к какому классу относятся методы
getItem, getSource и getItemSelectable, используемые в примерах этого параграфа, и что они делают).
Обработка событий мыши
Для того чтобы уметь обрабатывать события мыши, апплет должен
раскрывать интерфейсы
 MouseListener (содержит методы mouseClicked, mouseEntered,
mouseExited, mousePressed, mouseReleased) и/или
 MouseMotionListener (содержит методы mouseDragged и mouseMoved),
например
public class mouseEvents extends Applet
implements MouseListener, MouseMotionListener
В функции init данного апплета нужно назначить слушателя для отслеживания событий мыши. Обычно в роли слушателя выступает сам апплет:
public void init()
{
addMouseListener( this );
addMouseMotionListener( this );
}
47
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Мы рассматриваем апплет, который реагирует на следующие события:
нажатие (Up) и отпускание (Down) левой кнопки мыши, простое перемещение мыши (Move) и перемещение мыши при нажатой левой кнопке (Drag).
Информация о том, какое событие и с какими координатами произошло, записывается в следующие переменные:
String
String
String
String
mouseEvent;
mouseMoveEvent;
mouseDragEvent;
ctrl;
а затем выводится в окно апплета функцией paint.
Любой апплет, который раскрывает какой-либо интерфейс, должен
предоставлять код для всех методов этого интерфейса, даже если какой-либо
метод и не используется (в этом случае метод просто будет иметь пустое тело), например
public void mouseClicked(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
int x=e.getPoint().x;
int y=e.getPoint().y;
mouseEvent="mouse Up="+x+","+e.getPoint().y;
repaint();
}
public void mousePressed(MouseEvent e)
{
int x=e.getPoint().x;
int y=e.getPoint().y;
mouseEvent="mouse Down="+x+","+e.getPoint().y;
if (e.isControlDown())
ctrl="Control is down";
repaint();
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mouseMoved(MouseEvent e)
{
48
Практикум по программированию на языке Java
int x=e.getPoint().x;
int y=e.getPoint().y;
mouseMoveEvent="mouse Move="+x+","+y;
repaint();
}
public void mouseDragged(MouseEvent e)
{
int x=e.getPoint().x;
int y=e.getPoint().y;
mouseDragEvent="mouse Drag="+x+","+y;
repaint();
}
public void paint(Graphics g)
{
if (mouseEvent!=null)
{
g.drawString(mouseEvent, 5, 15);
mouseEvent=null;
}
if (mouseMoveEvent!=null)
{
g.drawString(mouseMoveEvent, 5, 35);
mouseMoveEvent=null;
}
if (mouseDragEvent!=null)
{
g.drawString(mouseDragEvent, 5, 55);
mouseDragEvent=null;
}
if (ctrl!=null)
{
g.drawString(ctrl, 5, 75);
ctrl=null;
}
}
Итак, при раскрытии интерфейса слушателя событий мыши требуется
переопределить все его абстрактные методы. Однако зачастую требуется обрабатывать только одно из событий мыши. Конечно, задавать пустые версии
всех остальных методов не очень приятно. Чтобы сэкономить время и не загромождать код пустыми реализациями методов, в пакете java.awt.event для
каждого интерфейса-слушателя включен реализующий его класс, называемый адаптером. Класс-адаптер предоставляет пустые версии всех методов
слушателя. Тогда программист должен создать класс, наследующий от класса-адаптера, переопределив только тот метод, который является обработчи49
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
ком требуемого события. В случае работы с событиями мыши классамиадаптерами являются MouseAdapter и MouseMotionAdapter, соответствующие интерфейсам MouseListener и MouseMotionListener.
Рассмотрим пример, когда требуется обработать только событие щелчка мыши и осуществить вывод строки, которая содержит координаты точки
щелчка.
Создается внутренний класс MouseClickedAdapter, в котором переопределяется только метод mouseClicked.
class mouseClickedAdapter extends MouseAdapter
{
public void mouseClicked(MouseEvent e)
{
int x=e.getPoint().x;
int y=e.getPoint().y;
mouseEvent="mouse click="+x+","+e.getPoint().y;
repaint();
}
}
Метод paint апплета тогда имеет следующий вид:
public void paint(Graphics g)
{
if (mouseEvent!=null)
{
g.drawString(mouseEvent, 5, 15);
mouseEvent=null;
}
}
Наконец, в качестве слушателя событиям мыши в методе init апплета
требуется назначить объект класса mouseClickedAdapter.
addMouseListener(new mouseClickedAdapter());
Задание для самостоятельной работы
Изучите самостоятельно класс MouseEvent, который используется при
передаче параметров в обработчики событий мыши, и его родительские классы (в частности, выясните, к какому классу относятся методы getPoint и
isControlDown, используемые в нашем примере, и что они делают).
50
Практикум по программированию на языке Java
Обработка событий клавиатуры
Для того чтобы уметь обрабатывать события клавиатуры, апплет должен раскрывать интерфейс
 KeyListener (события keyTyped, keyReleased и keyPressed),
например
public class keyEvents extends Applet implements KeyListener
В функции init данного апплета нужно назначить слушателя для отслеживания событий клавиатуры. Обычно в роли слушателя выступает сам
апплет:
public void init()
{
addKeyListener( this );
}
Мы рассматриваем апплет, который реагирует на следующие события:
нажатие (Up) и отпускание (Down) клавиши на клавиатуре. Информация о
том, какое событие произошло, записывается в следующие переменные:
String keyDownEvent;
String keyUpEvent;
а затем выводится в окно апплета функцией paint.
Любой апплет, который раскрывает какой-либо интерфейс, должен
предоставлять код для всех методов этого интерфейса, даже если какой-либо
метод и не используется (в этом случае метод просто будет иметь пустое тело), например
public void keyTyped ( KeyEvent e )
{
}
public void keyReleased ( KeyEvent e )
{
keyUpEvent="Отпущена кнопка "+
51
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
e.getKeyText(e.getKeyCode())+
", буква "+e.getKeyChar();
repaint();
}
public void keyPressed ( KeyEvent e )
{
keyDownEvent="Нажата кнопка "+
e.getKeyText(e.getKeyCode())+
", буква "+e.getKeyChar();
repaint();
}
public void paint(Graphics g)
{
if (keyDownEvent!=null)
{
g.drawString(KeyDownEvent, 5, 15);
keyDownEvent=null;
}
if (keyUpEvent!=null)
{
g.drawString(KeyUpEvent, 5, 45);
keyUpEvent=null;
}
}
Задание для самостоятельной работы
Изучите самостоятельно класс KeyEvent, который используется при
передаче параметров в обработчики событий мыши (в частности, выясните,
что делают методы getKeyText, getKeyCode и getKeyChar, используемые в
нашем примере).
Работа с потоками
В терминах операционной системы выполняющиеся программы называются процессами. Процесс можно разбить на отдельно выполняемые подпроцессы, или потоки. Каждый поток выглядит и выполняется так, как если
бы он был единственным в программе. Он также может взаимодействовать с
другими потоками, но для этого требуется обеспечить контроль доступа к
совместно используемым переменным.
Для создания потоков можно использовать разные способы.
52
Практикум по программированию на языке Java
Первый способ. Подходит
для создания одного потока. В
нашем апплете “бегущая строка”
перемещается из правого верхнего
угла в левый нижний. Мы можем
лишь заставить стандартный поток
класса Thread "засыпать" на заданное время, другие характеристики потока мы задать не можем.
Для работы с потоком апплет должен раскрывать интерфейс Runnable,
например
public class Potok extends Applet implements Runnable
Далее, в рамках апплета нужно создать объект Thread, связав его с апплетом, например,
Thread marqThread = new Thread(this);
и запустить этот поток в методе start апплета:
public void start()
{
marqThread.start();
}
Поскольку данный апплет раскрывает интерфейс Runnable, он должен
предоставлять код для его единственного метода run, в котором и определяются основные действия с данным потоком, например,
public void run()
{
while (true)
{
x-=1;
if (x==0)
53
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
x=size().width;
repaint();
try
{
marqThread.sleep(100);
}
catch(InterruptedException e)
{
}
}
}
public void paint(Graphics g)
{
g.setColor(Color.red);
g.drawString("Изучаем JAVA!!!", x,y);
}
Как
видим,
в
методе
run
обрабатывается
исключение
InterruptedException. Дело в том, что для создания паузы в работе потока
мы вызываем его метод sleep, который бросает это исключение, если "покой"
потока был нарушен каким-либо образом до окончания данной паузы. Поэтому мы вынуждены обрабатывать здесь это исключение – в данном случае
обработка заключается в том, что мы его просто игнорируем.
Второй способ.
Подходит
для создания нескольких потоков.
Мы можем определять свойства и
поведение потока. В нашем примере два потока последовательно
выводят надписи на экран, один –
в направлении с северо-запада на
юго-восток, другой – в направлении с юго-запада на северо-восток.
Создадим внутренний класс, производный от Thread:
class MyThread extends Thread
{
int x,y;
// координаты сообщения
int pause;
// длительность паузы
54
Практикум по программированию на языке Java
String mes, move;
Color col;
// сообщение и направление движения:
// se - юго-восток, we - северо-восток
// цвет
// методы
// ...
}
Конструктор потока получает параметры извне и инициализирует все
переменные потока. Обратите внимание, что все объекты явно создаются командой new.
MyThread(String mes, int col, String move, int pause)
{
this.mes= new String(mes);
this.col= new Color(col);
this.pause=pause;
this.move= new String(move);
if (move.equals("se"))
{ x=0; y=0; }
else if (move.equals("ne"))
{ x=0;
y=getSize().height; }
}
Метод run определяет поведение потока. Он никогда не вызывается
явно, его вызывает метод start потока.
public void run()
{
while (true)
// бесконечный цикл!
{
if (move.equals("se"))
{ // движемся на юго-восток
x+=10;
y+=10;
if (x>=getSize().width)
{ // дошли до правого края апплета
x=0;
y=0;
}
}
else if (move.equals("ne"))
{ // движемся на северо-восток
x+=10; y-=10;
if (x>=getSize().width)
{ // дошли до правого края апплета
x=0;
y=getSize().height;
}
}
mypaint();
// вызываем метод рисования
try
55
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
// поток засыпает на заданное время
{
MyThread.sleep(pause); }
catch( InterruptedException e )
{
}
}
}
Для очередного вывода сообщения создадим пользовательский метод
mypaint. Он получает контекст устройства из апплета с помощью метода
getGraphics (наш поток имеет доступ к апплету, поскольку принадлежит к
внутреннему классу).
public void mypaint()
{
Graphics g=getGraphics();
if (g!=null)
{
g.setColor(col);
g.drawString(mes, x,y);
}
}
В классе апплета содержится только одна функция – init. В этой функции создается два объекта-потока (обратите внимание, что они оба безымянные), и эти потоки сразу же запускаются на выполнение вызовом их метода
start.
public class potoki extends Applet
{
public void init()
{
new myThread("Изучаем Java",0xff5555, "se", 700).start();
new myThread("Изучаем Java",0x5555ff, "ne", 1000).start();
}
class MyThread extends Thread
{
// определение класса MyThread
}
}
Бывают ситуации, когда два и более различных потока должны получать доступ к одним и тем же переменным. Тогда программисту требуется
обеспечить контроль доступа к этим переменным, другими словами, синхро56
Практикум по программированию на языке Java
низировать действия потоков. Существуют два уровня обеспечения синхронизации:

защита совместно используемых ресурсов;

сигнализация об изменениях между потоками.
В случае применения первого уровня синхронизации обновление сов-
местно используемых переменных производится только в методах (внешних
относительно класса-потока), отмеченных специальным модификатором
synchronized. Такие методы называются синхронизированными. Поток, который вызывает синхронизированный метод, получает исключительный доступ к объекту, которому этот метод принадлежит, тем самым накладывая
блокировку на объект. Пока объект не будет разблокирован, ни один другой
поток не может получить доступ ни к одному синхронизированному методу
данного объекта. Снятие блокировки с объекта происходит в момент, когда
закончит работу синхронизированный метод, вызвавший блокировку. Пока
объект является заблокированным одним потоком, другие потоки помещаются в очередь и ожидают, когда они смогут продолжить работу. Модификатором synchronized можно отмечать не только методы, но и отдельные блоки
(составные операторы). При этом в круглых скобках следует указать ссылку
на объект, который нужно заблокировать, например: synchronized (this).
Второй уровень обеспечения синхронизации подразумевает использование механизма передачи сообщений потоку о том, что уже выполнено
условие, исполнение которого он ожидает. Это достигается за счет использования трех методов: wait, notify и notifyAll.
Если поток обнаружит, что не может продолжать работу, поскольку
совместно используемая переменная не имеет надлежащего значения, он
должен вызвать метод wait из синхронизированного метода или блока. После
этого поток снимает блокировку объекта, которому принадлежит синхронизированный метод, и помещается в очередь данного объекта.
57
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Поток, изменивший значение совместно используемой переменной,
должен проинформировать об этом другие потоки, которые были помещены
в очередь соответствующего объекта посредством вызова метода wait. Это
делается с помощью методов notify и notifyAll. Первый из них "пробуждает"
один из потоков, ожидающих возможности доступа к совместно используемой переменной. Какой именно поток из очереди будет "пробужден", зависит
от политики потоков, реализованной в виртуальной Java-машине. Обычно
используется одна из двух стратегий: политика учета приоритетов потоков и
политика циклического планирования. Согласно первой политике каждому
потоку назначается свой приоритет, и первыми продолжат свою работу потоки с наибольшим приоритетом. Вторая политика предполагает, что потоки
пробуждаются в том порядке, в котором попали в очередь. "Пробудившийся"
поток снова встает в очередь в случае невыполнения условия продолжения
работы.
В отличие от метода notify, который "пробуждает" только один поток
из очереди, метод notifyAll "пробуждает" все потоки, находящиеся в очереди, предоставляя им возможность состязаться за блокировку объекта.
Типовым примером использования этих методов является организация
очереди на обслуживание. Допустим, моделируется работа парикмахерской,
в которой работает 4 парикмахера. Создаются два класса – внешний класс
отвечает за работу парикмахерской, внутренний класс – класс, производный
от класса Thread, моделирует поведение клиента. Внешний класс имеет переменную, например, count, которая в каждый момент времени будет хранить количество клиентов, которые обслуживаются в данный момент. Очевидно, это количество не должно превосходить 4.
При появлении нового клиента вызывается синхронизированный метод
внешнего класса, в котором анализируется, существует ли свободное место.
Если такового нет, то клиент ставится в очередь с помощью метода wait. По
окончании обслуживания какого-либо клиента должен вызываться другой
58
Практикум по программированию на языке Java
синхронизированный метод, который, в частности, будет уведомлять о том,
что появилось свободное место.
class Queue extends Applet
{
int count=0;
// другие данные и методы класса
// ...
// метод, обрабатывающий появление нового клиента
// параметр метода – идентификатор клиента
synchronized void enter(int num)
{
if(count==4)
{
// постановка клиента в очередь
try
{
wait();
}
catch(InterruptedException e){}
}
// выйдет из ожидания только при появлении
// свободного места
count++;
}
// метод, обрабатывающий окончание обслуживания клиента
synchronized void leave()
{
count--;
// оповещает другие потоки о появлении
// свободного места
notify();
}
class Client extends Thread
{
int id;
// идентификатор клиента
// разные данные и методы класса
// ...
public void run()
{
enter(id);
// появился клиент
// другие действия клиента
// ...
leave();
// конец обслуживания
}
}
}
59
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Задание для самостоятельной работы
Рассмотрите самостоятельно пример 1 из приложения 5, иллюстрирующий совместное использование ресурсов и синхронизацию при работе с потоками.
60
Практикум по программированию на языке Java
Глава 4. Разработка приложений
Создание первого приложения
Создадим приложение типа "Hello, world!". Для его создания выполним
следующие действия:
Сначала создадим исходный текст приложения. Запустим редактор
JCreator и наберем в нем текст:
import java.io.*;
class JavaNow
{
public static void main(String[] args)
{
System.out.println("Изучаем JAVA!!!");
}
}
В созданном классе имеется единственная функция - обязательная
функция main. Она является точкой входа, с которой начинается выполнение
приложения. Эта функция может получать аргументы извне.
Единственное действие, которое выполняется в данном приложении печать строки "Изучаем JAVA" в выходной поток.
В этом приложении используется стандартный поток вывода (по
умолчанию он связан с консолью). Этот поток называется out, имеет тип
PrintStream и является статической переменной класса System. Для печати
используются методы print и println, параметры которых могут быть любых
стандартных типов.
"Симметричный" ему стандартный поток ввода (по умолчанию он
также связан с консолью) называется in, имеет тип InputStream и является
статической переменной класса System. Самая простая функция чтения read
без параметров читает и возвращает один символ из входного потока.
61
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Вспомним, что любой файл c исходным текстом на Java должен иметь
расширение .java. Кроме того, он должен иметь имя, совпадающее с именем
класса. Поэтому следует сохранить файл под именем JavaNow.java.
Включим файл в проект и откомпилируем. Если в файле были синтаксические ошибки, будут выданы соответствующие сообщения. Если ошибок
не было, будет создан файл JavaNow.class. Запустим приложение на выполнение. Ваше первое приложение создано.
Задания для самостоятельной работы
1.
Создайте приложение, которое получает и использует параметры
командной строки.
2.
Рассмотрите самостоятельно элементы класса System, которые
связаны со стандартным вводом-выводом информации.
3.
Создайте приложение, которое использует входной поток
System.in. Например, можете написать программу перевода градусов Фаренгейта в градусы Цельсия, которая запрашивает информацию из стандартного
потока ввода.
Работа с исключениями
Исключением называется нештатная ситуация, возникающая при выполнении программы и требующая какой-либо обработки. Технология исключений разработана специально для объектно-ориентированных языков,
поскольку исключение может возникать в одном классе, а обрабатываться
совершенно в другом.
В некоторых случаях исключения может генерировать сама Javaмашина, однако чаще всего их инициируют методы. Если метод "бросает"
исключение, это должно быть явно указано в его заголовке, например:
static int SomeFunction(String s) throws TestException
62
Практикум по программированию на языке Java
Генерация исключения заключается в выполнении команды throw, параметром которой является объект класса Exception или производного от него класса, например
throw new TestException("Test message");
Для обработки исключений используется команда
try
{ ... }
catch (тип_исключения1 переменная1)
{ ... }
...
catch (тип_исключенияN переменнаяN)
{ ... }
finally
{ ... }
В блок try мы помещаем команды, которые потенциально могут вызвать исключение. В частности, если мы вызываем метод, который "бросает"
исключение, его вызов обязательно следует поместить в блок try. При возникновении исключения оставшаяся часть блока try игнорируется.
Каждый блок catch предназначен для обработки исключения какоголибо типа. При возникновении исключения срабатывает только один блок,
первый из подходящих блоков для данного типа исключений. Блок считается
подходящим, если его параметр имеет тот же тип, что и возникшее исключение, или соответствует одному из родительских классов этого исключения.
Запрещается помещать блок catch с исключением родительского класса до
блока с исключением производного класса, поскольку в этом случае второй
никогда не сработает.
Блок finally выполняется в любом случае, возникло ли исключение,
или нет.
В библиотеке классов JDK есть много разных классов исключений. В
своих программах можно использовать эти классы или разрабатывать собственные. Для всех исключений родительским классом является класс
Exception.
63
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Мы, в основном, будем сталкиваться со стандартными исключениями.
Типичной является ситуация, когда мы вызываем какую-либо функцию, которая может генерировать исключение. Обрабатывать эту ситуацию можно
одним из двух способов:
 поместить вызов "опасной" функции в блок try ... catch. Тем самым
мы берем обработку на себя. Например, при открытии файла с помощью
объекта
FileInputStream
может
возникать
исключение
FileNotFoundException в том случае, если файла с заданным именем не существует. Обработка может выглядеть так:
class Test
{
public static void main(String[] args)
{
try
{
FileInputStream in = new FileInputStream("test.txt");
// дальнейшая обработка файла
}
catch (FileNotFoundException e)
{
System.out.println("Файл не найден!!!");
}
}
}
 в
заголовок
нашей
функции
добавить
выражение
throws
SomeException, например,
public static void main(String[] args)
throws FileNotFoundException
В этом случае исключение будет передаваться на уровень выше. Если это
уровень виртуальной машины Javа (как в данном примере), то исключение
будет обрабатывать она (это не очень хороший подход – мы сваливаем свою
работу на другого). При этом зачастую виртуальная машина выполняет аварийное завершение программы, что тоже не очень-то хорошо. Полностью
этот класс будет выглядеть так.
64
Практикум по программированию на языке Java
class Test
{
public static void main(String[] args)
throws FileNotFoundException
{
FileInputStream in = new FileInputStream("test.txt");
// дальнейшая обработка файла
}
}
Задание для самостоятельной работы
Изучите самостоятельно стандартные классы исключений, имеющиеся
в библиотеке JDK.
Работа с файлами
В языке Java есть довольно много классов для работы с файлами и
прочими потоками ввода/вывода, они расположены в пакете java.io. Коротко
рассмотрим некоторые из них.
Класс File
служит для получения информации о файлах и каталогах. Для создания объекта этого класса есть 3 конструктора, чаще всего используется следующий:
File(String имяФайла)
создание объекта 'файл' по его имени в
файловой системе
В этом классе есть много полезных функций, например,
isDirectory()
проверяет, является ли данный объект каталогом; возвращает логическое значение
isFile()
проверяет, является ли данный объект файлом; возвращает логическое значение
lastModified()
возвращает дату последней модификации
файла
65
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
canRead()
проверяет, можно ли читать данные из
файла; возвращает логическое значение
canWrite()
проверяет, можно ли записывать данные в
файл; возвращает логическое значение
delete()
удаляет файл
Следующие 7 классов используются для работы с двоичными файлами.
Класс FileInputStream
(производный от InputStream)
простейший класс для потокового (последовательного) чтения данных из
файла. Объект этого класса создается на основе объекта File или по имени
файла в файловой системе, например,
FileInputStream myfile = new FileInputStream("data.txt");
Для чтения данных используются методы
read()
читает и возвращает один символ из файла
read(byte [] массив)
читает и возвращает массив символов из
файла
Для закрытия файла есть метод close, определенный в родительском
классе InputStream.
Класс FileOutputStream
(производный от OutputStream)
простейший класс для потокового (последовательного) вывода данных в
файл. Объект этого класса создается на основе объекта File или по имени
файла в файловой системе, например,
FileOutputStream myfile = new FileOutputStream("data.txt");
Для записи данных используются методы
66
Практикум по программированию на языке Java
write(int символ)
печатает один символ в файл (обратите
внимание, что тип параметра – int!)
write(byte [] массив)
печатает массив байтов в файл
Для закрытия файла есть метод close, определенный в родительском
классе OutputStream.
Довольно неудобно читать и записывать данные в файл по отдельным
байтам. Следующие два класса служат своеобразными "оболочками" для
предыдущих классов и позволяют оперировать данными всех стандартных
типов.
Класс DataInputStream
(производный от InputStream)
простейший класс для потокового (последовательного) чтения данных стандартных типов из файла. Объект этого класса создается на основе объекта
InputStream, т.е., например,
DataInputStream myfile = new DataInputStream( new
FileInputStream("data.txt"));
Для всех стандартных типов данных определены методы:
readInt()
readFloat()
readChar()
и т.п.
читает значение указанного типа
Для чтения не-латинских букв можно порекомендовать функцию
readUTF.
Класс DataOutputStream
(производный от OutputStream)
67
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
простейший класс для потоковой (последовательной) записи данных стандартных типов в файл. Объект этого класса создается на основе объекта
OutputStream, т.е., например,
DataOutputStream myfile =
new DataOutputStream( new FileOutputStream("data.txt"));
Для всех стандартных типов данных определены методы:
writeInt(int число)
writeFloat(float число)
writeChar(char символ)
writeChars(String строка)
и т.п.
печатает значение указанного типа
Для печати не-латинских букв можно порекомендовать функцию
writeUTF.
Классы BufferedInputStream и BufferedOutputStream
используют буферизованный ввод-вывод, поэтому работают более эффективно, чем, например, FileInputStream и FileOutputStream.
Класс RandomAccessFile
класс для работы с файлами произвольного доступа, используется как для
чтения, так и для записи. Объект этого класса создается на основе объекта
File или по имени файла в файловой системе. Имеет конструкторы:
RandomAccessFile( String
имяФайла, String режим )
RandomAccessFile( File файл,
String режим )
параметр "режим" может принимать значения:
"r" – только для чтения и
"rw" - для чтения и записи
Для всех стандартных типов данных определены методы:
readInt()
readFloat()
readChar()
68
читает значение указанного типа
Практикум по программированию на языке Java
и т.п.
writeInt(int число)
writeFloat(float число)
writeChar(char символ)
writeChars(String строка)
и т.п.
печатает значение указанного типа
Для смещения внутреннего указателя в любое место файла используется метод
seek(long смещение)
смещение задается в байтах от начала файла
Следующие 6 классов используются для работы с текстовыми файлами.
Классы InputStreamReader и OutputStreamWriter
(производные от Reader и Writer)
простейшие классы для чтения/записи из/в текстовый файл. Объекты этих
классов создаются на основе объектов InputStream и OutputStream. Например,
InputStreamReader myfile =
new InputStreamReader( new FileInputStream("data.txt"));
OutputStreamWriter myfile =
new OutputStreamWriter( new FileOutputStream("data.txt"));
Оба класса имеют функцию getEncoding для получения текущей кодовой страницы (cp1251 – кодовая страница для русского Windows), а также
конструкторы, которые позволяют задать нужную кодовую страницу в качестве второго параметра. Имеются методы read и write для чтения/записи
символа (строки символов).
Классы FileReader и FileWriter
(производные от InputStreamWriter и OutputStreamWriter)
69
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
отличаются от InputStreamWriter и OutputStreamWriter тем, что объекты
этих классов создаются на основе объекта File или по имени файла в файловой системе, т.е.
FileReader myfile = new FileReader("data.txt");
FileWriter myfile = new FileWriter("data.txt");
а также не имеют конструктора с заданием кодовой страницы.
Классы BufferedReader и BufferedWriter
(производные от Reader и Writer)
используются для буферизованного ввода-вывода данных, поэтому их использование более эффективно, чем, например, FileReader и FileWriter. Создаются на основе объектов Reader и Writer, например,
BufferedReader myfile = new BufferedReader ( new
FileReader("data.txt"));
BufferedWriter myfile = new BufferedWriter ( new
FileWriter("data.txt"));
В классе BufferedReader определен метод readLine, который позволяет читать строку символов.
Классы PipedInputStream и PipedOutputStream
классы для канальных потоков ввода-вывода. Эти классы служат для создания коммуникационных каналов между процессами. Эти каналы не имеют
физической основы в виде файлов, т.е. данные, передаваемые по этим каналам, не сохраняются в долгосрочной памяти. Объекты канального потока вывода (PipedOutputStream) и канального потока ввода (PipedInputStream)
используются совместно – один из них на стороне процесса, передающего
данные, другой – на стороне процесса, принимающего данные. Пример апплета, использующего канал ввода-вывода (пример 2), приведен в приложении 5.
70
Практикум по программированию на языке Java
Несколько замечаний по работе с русским текстом.
1. Классы FileWriter и FileReader работают с русскими буквами без
проблем:
Запись:
FileWriter myfile = new FileWriter("data.txt");
myfile.write("proba pera --- проба пера");
Чтение:
FileReader myfile = new FileReader("data.txt");
char buf[] =new char[80];
myfile.read(buf);
System.out.println(buf);
2. Классы BufferedWriter и BufferedReader также работают с русскими буквами без проблем:
Запись:
BufferedWriter myfile = new BufferedWriter(new
FileWriter("data.txt"));
myfile.write("proba pera --- проба пера");
Чтение:
BufferedReader myfile = new BufferedReader (new
FileReader("data.txt"));
System.out.println(myfile.readLine());
3. Функции readUTF и writeUTF классов DataInputStream и
DataOutputStream позволяют записывать и читать русские символы в/из
файла. При этом получившийся файл будет нечитабельным в текстовых редакторах.
Запись:
DataOutputStream myfile =
new DataOutputStream( new FileOutputStream("data.txt"));
myfile.writeUTF("proba pera --- проба пера");
Чтение:
DataInputStream myfile =
new DataInputStream( new FileInputStream("data.txt"));
System.out.println(myfile.readUTF());
4. Классы OutputStreamWriter и InputStreamReader позволяют явно
указывать кодировку символов, например,
71
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Запись:
OutputStreamWriter myfile =
new OutputStreamWriter(
new FileOutputStream("data.txt"),"KOI8-R");
System.out.println(myfile.getEncoding());
myfile.write("proba pera --- проба пера");
Чтение:
InputStreamReader myfile =
new InputStreamReader(
new FileInputStream("data.txt"), "KOI8_R");
System.out.println(myfile.getEncoding());
char buf[] =new char[80];
myfile.read(buf,0,80);
System.out.println(buf);
При этом получившийся файл, разумеется, можно читать в текстовых
редакторах.
Задание для самостоятельной работы
Изучите более подробно классы, рассмотренные выше, а также классы
исключений, которые содержатся в пакете java.io.
Сериализация
При работе с файлами (так же, как и с другими потоками ввода и вывода) можно использовать специальную технологию сохранения данных, называемую сериализацией. Главное отличие сериализации заключается в том,
что сохраняется в файл не текст, а объект какого-либо класса, например, заполненный данными список типа Choice, массив и прочие. Сериализация заключается в преобразовании объекта в простую последовательность байтов
таким образом, чтобы впоследствии он мог быть корректно восстановлен.
При сериализации должны быть учтены следующие моменты. Объект,
подлежащий сериализации, должен раскрывать интерфейс Serializable. Этот
интерфейс уже раскрыт в базовом классе Object. Поэтому объекты всех
72
Практикум по программированию на языке Java
классов, которые наследуют от класса Object, могут быть сериализованы.
Это значит, что для сериализации этих объектов могут применяться методы
writeObject
и
readObject
классов
ObjectOutputStream
и
ObjectInputStream. При создании объектов классов ObjectOutputStream и
ObjectInputStream конструкторам передается в качестве параметра объект
потока, связанного с файлом или сетевым приложением. Метод writeObject
класса ObjectOutputStream принимает в качестве параметра сериализуемый
объект. Метод readObject класса ObjectInputStream возвращает значение
типа Object, после чего можно восстановить структуру первоначального
объекта посредством преобразования к необходимому типу данных.
Пусть, например, в приложении формируется список выбора
Choice sel;
При необходимости можно список сериализовать в файл.
try
{
ObjectOutputStream
fileWrite=new ObjectOutputStream
(new FileOutputStream("myfile"));
fileWrite.writeObject(sel);
System.out.println("Ввод завершен\n");
fileWrite.close();
}
catch(IOException e1)
{
System.out.println("Ошибка ввода или вывода\n");
}
Соответственно, при необходимости можно восстановить состояние
списка из файла.
try
{
ObjectInputStream fileRead=
new ObjectInputStream
(new FileInputStream("myfile"));
// удаляем старые данные из списка
sel.removeAll();
// восстанавливаем содержимое из файла
73
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
// создаем временный объект для чтения из файла
Choice c=new Choice();
c=(Choice)fileRead.readObject();
// помещаем в элемент управления список данные из
// списка с
for(int i=0; i<c.countItems(); i++)
sel.addItem(c.getItem(i));
System.out.println("Данные заполнены\n");
}
catch (IOException e)
{
System.out.println("Ошибка ввода или вывода\n");
}
catch(ClassNotFoundException e)
{
System.out.println
("Ошибка при преобразовании типа\n");
}
При выполнении процесса сериализации требуется обрабатывать исключительные ситуации, связанные с ошибками ввода-вывода (например, задание некорректного имени файла) и ошибками класса (не удается осуществить преобразование типа). Поэтому должны быть перехвачены исключительные ситуации IOException и ClassNotFoundException.
Работа с базами данных
Как Java-приложения, так и Java-апплеты могут работать с базами
данных. Для работы с базами данных:
 или подключают специализированную библиотеку классов для конкретной СУБД – ORACLE, MySQL и т.п.;
 или используют интерфейс ODBC.
В обоих случаях применяется технология JDBC (Java Database
Connectivity), представляющая собой программный интерфейс для выполнения SQL-запросов к реляционным базам данных из Java-программ. Библиотека классов JDBC находится в пакете java.sql. Рассмотрим оба способа более подробно.
74
Практикум по программированию на языке Java
Работа с базой данных MySQL
Мы будем использовать пакет классов для MySQL, который называется mysql-connector-java-3.0.8-stable-bin.jar. Существуют два подхода:
 этот файл нужно поместить в каталог расширений JRE, т.е., в
C:\j2sdk1.4.0\jre\lib\ext;
 или же этот файл можно поместить в любой каталог и подключить в
свойствах JCreator. Выберите меню "Configure" – "Options...", затем строку “JDK Profiles”, выберите профиль и нажмите кнопку “Edit”, затем в появившемся окне добавьте нужный архив или каталог.
Рассмотрим простой пример работы с СУБД MySQL. Консольное приложение печатает все фамилии из таблицы "Сотрудники". Сначала подключим нужные пакеты. Из пакета MySQL достаточно подключить единственный класс - Driver.
import java.sql.* ;
import com.mysql.jdbc.Driver ;
import java.io.*;
В функции main определим переменную типа Connection. Объект
Connection нужен для создания соединения с базой данных.
Дальнейший код следует поместить в блок try, поскольку некоторые из
используемых методов могут вызывать исключения.
Регистрируем драйвер с помощью статического метода registerDriver
класса DriverManager.
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Создаем соединение с базой данных. Обратите внимание, мы не создаем объект с помощью операции new. Метод getConnection сам создает объект и возвращает ссылку на него.
75
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
c = DriverManager.getConnection
("jdbc:mysql://localhost/mycross", "root", "");
Параметры метода getConnection:
1.
cтрока подключения:
 jdbc:mysql - имя протокола подключения,
 localhost - имя хоста,
 mycross - имя базы данных;
2.
логин для подключения к базе данных;
3.
пароль для подключения к базе данных.
После успешного подключения создаем объект Statement, который будет использоваться для выполнения запросов к базе данных (здесь тоже не
используем new):
Statement s = c.createStatement();
Объявляем переменную ResultSet для получения результата запроса:
ResultSet rs = null;
Выполняем запрос:
rs = s.executeQuery("SELECT * FROM personal");
Заметим, что для выполнения различных запросов, связанных с изменением данных (INSERT, UPDATE, DELETE и пр.), следует применять метод
executeUpdate, например,
s.executeUpdate("DELETE FROM personal");
Затем в цикле последовательно перебираем и печатаем строки результата:
while( rs.next() )
76
Практикум по программированию на языке Java
{
str=rs.getString("name");
System.out.println(str);
}
Перед выходом из программы закрываем объекты ResultSet и
Connection.
rs.close();
rs=null;
c.close();
с=null;
И, наконец, посмотрим, как выглядит обработка исключений, которые
могут здесь возникнуть.
catch (SQLException ex)
{
System.out.println("SQLException caught");
System.out.println("---");
while ( ex != null ){
System.out.println("Message
: " + ex.getMessage());
System.out.println("SQLState : " + ex.getSQLState());
System.out.println("ErrorCode : " + ex.getErrorCode());
System.out.println("---");
ex = ex.getNextException();
}
}
catch (Exception ex)
{
System.out.println("Other Error in Main.");
}
Примечание: Для того чтобы из базы данных MySQL корректно читались русские буквы, в файл my.ini, который находится в каталоге
C:\WINDOWS, нужно добавить строки:
[mysqld]
default-character-set=win1251
Использование интерфейса ODBC
Интерфейс ODBC, по существу, является посредником между приложением и базой данных. Ниже рассматривается пример таблицы “гостевая
77
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
книга”. В качестве "моста" здесь используется ODBC-JDBC Bridge Driver от
Sun Microsystems, загружаемый с помощью метода forName класса Class.
Это альтернативный способ регистрации драйвера.
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch(ClassNotFoundException e)
{
System.out.println("Sorry! Class is not found!");
}
Теперь нужно указать источник данных (в нашем примере он называется guest):
String mySource="jdbc:odbc:guest";
и создать соединение:
Connection databaseConnection=
DriverManager.getConnection(mySource);
Теперь, когда соединение уже создано, нам, как и в первом примере,
нужно создать объект Statement для выполнения запросов к таблицам базы
данных. Результат выполнения запроса сохраним в переменной ResultSet.
Statement mystatement=databaseConnection.createStatement();
String mySQL="SELECT * FROM guest";
ResultSet rec=mystatement.executeQuery(mySQL);
Заметим, что, выполнив SQL-запрос к таблице, мы можем получить так
называемые метаданные, т.е. сведения о самой таблице, такие как ее имя, количество столбцов, их названия и тип, количество знаков после запятой в
данных и т.д. В нашем примере мы делаем это так:
ResultSetMetaData mymetadata=rec.getMetaData();
String mytable=mymetadata.getTableName(1);
System.out.println("The table is "+mytable+".");
78
Практикум по программированию на языке Java
int mycolumns=mymetadata.getColumnCount();
System.out.println("The number of columns is "+mycolumns+".");
System.out.println("They are: ");
for(int i=1;i<=mycolumns;i++)
{
String mycurColName=mymetadata.getColumnLabel(i);
// название столбца
int mycurColType=mymetadata.getColumnType(i);
// тип столбца
int mycurColScale=mymetadata.getScale(i);
// размер столбца
System.out.println(mycurColName+" (type: "+
mycurColType+", scale: "+mycurColScale+" )");
}
Наконец, мы организуем цикл для вывода содержимого таблицы в выходной поток:
while (rec.next())
{
String myrow="\n";
for(int i=1;i<=mycolumns;i++)
{
myrow+=rec.getString(i)+" ";
}
System.out.println(myrow);
}
Задание для самостоятельной работы
Рассмотрите более подробно
классы Connection, Statement и
ResultSet.
Создание оконных приложений
Для создания оконных приложений удобнее всего использовать класс
Frame. В иерархии классов он выглядит следующим образом:
java.lang.Object
|
+----java.awt.Component
|
+----java.awt.Container
79
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
|
+----java.awt.Window
|
+----java.awt.Frame
По своей природе этот класс похож на классы Applet и Panel. В объекте класса Frame можно размещать элементы управления. Класс, производный от Frame, может раскрывать интерфейсы ActionListener и ItemListener.
Простейшее оконное приложение имеет вид:
// Простое оконное приложение
import java.awt.event.*;
import java.awt.*;
class SimpleFrame extends Frame
{
public static void main(String[] args)
{
SimpleFrame a= new SimpleFrame();
}
SimpleFrame()
{
setTitle("Оконное приложение");
setSize(400,200);
show();
}
}
На первый взгляд структура программы не совсем привычна – в функции main создается объект того же класса, к которому принадлежит функция
main. Зато при создании объекта можно вызвать конструктор с параметрами.
Если вы запустите это приложение, то сразу же обратите внимание, что
кнопка закрытия окна не работает. Для обработки событий окна нужно писать специальный код. Он может выглядеть, например, так (код располагается в конструкторе)
addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
dispose();
80
Практикум по программированию на языке Java
System.exit(0);
}
});
Это пример определения безымянного класса. Рассмотрим его подробнее. Мы вызываем метод addWindowListener для того, чтобы назначить
слушателя оконных событий. В качестве параметра создаем и используем
объект класса WindowAdapter. Но этот класс является абстрактным! Поэтому мы неявно создаем производный от него класс и переопределяем нужные
нам методы – в данном случае обработку события закрытия окна (метод
dispose уничтожает объект Frame). После этого останавливаем виртуальную
машину Java вызовом метода System.exit(0). При компиляции будет создан
класс с именем SimpleFrame$1.class.
Важным преимуществом объектов класса Frame и его производных
является то, что они могут содержать строку меню. Для создания меню используются классы MenuBar, Menu и MenuItem (операции по созданию меню удобно выполнять в конструкторе). Приведем пример простого оконного
приложения с меню:
// Простое оконное приложение с меню
import java.awt.event.*;
import java.awt.*;
class WinApp2 extends Frame
implements ActionListener, ItemListener
{
MenuBar mb;
Menu m1;
MenuItem ID_OPEN;
MenuItem ID_SAVE;
MenuItem delim1;
MenuItem ID_EXIT;
Menu m2;
MenuItem ID_NEW;
MenuItem ID_CASCADE;
MenuItem ID_TILE;
Menu m3;
CheckboxMenuItem ID_CHECK;
81
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
String status="";
Label l = new Label("");
public static void main(String[] args)
{
WinApp2 wapp= new WinApp2();
}
WinApp2()
{
mb=new MenuBar();
setMenuBar(mb);
setTitle("Оконное приложение");
m1=new Menu("File");
mb.add(m1);
m2=new Menu("Window");
mb.add(m2);
m3=new Menu("Tools");
mb.add(m3);
ID_OPEN=new MenuItem("Open");
ID_OPEN.addActionListener(this);
m1.add(ID_OPEN);
ID_SAVE=new MenuItem("Save");
ID_SAVE.addActionListener(this);
m1.add(ID_SAVE);
delim1=new MenuItem("-");
m1.add(delim1);
ID_EXIT=new MenuItem("Exit");
ID_EXIT.addActionListener(this);
m1.add(ID_EXIT);
ID_NEW=new MenuItem("New window");
ID_NEW.addActionListener(this);
m2.add(ID_NEW);
ID_CASCADE=new MenuItem("Cascade");
ID_CASCADE.addActionListener(this);
m2.add(ID_CASCADE);
ID_TILE=new MenuItem("Tile");
ID_TILE.addActionListener(this);
m2.add(ID_TILE);
ID_CHECK=new CheckboxMenuItem ("Check spelling");
ID_CHECK.addItemListener(this);
m3.add(ID_CHECK);
add(l);
addWindowListener(
new WindowAdapter()
{ public void windowClosing(WindowEvent e)
{
dispose();
82
Практикум по программированию на языке Java
System.exit(0);
}
});
setSize(400,200);
setBackground(new Color(200,200,220));
setLocation(200,200);
show();
}
public void actionPerformed(ActionEvent e)
{
l.setText(e.getActionCommand());
if(e.getActionCommand().equals("Exit"))
{
dispose();
System.exit(0);
}
}
public void itemStateChanged(ItemEvent e)
{
if (e.getStateChange()==ItemEvent.SELECTED)
l.setText(""+e.getItem()+" включен");
else
l.setText(""+e.getItem()+" выключен")
}
}
Линейка меню задается с помощью объекта MenuBar. Этот объект
нужно сначала создать, а потом назначить объекту Frame с помощью метода
setMenuBar.
Пункты меню (имеющие подчиненные меню) – это объекты класса
Menu. Их нужно сначала создать, а потом добавить в объект MenuBar с помощью метода add.
Конечные пункты меню – это объекты класса MenuItem. Они конструируются в три шага. Такой объект сначала нужно создать, потом назначить
ему слушателя методом addActionListener, а затем добавить в объект Menu
с помощью метода add.
Для обработки событий меню класс, производный от Frame, должен
раскрывать интерфейс ActionListener. Принципы работы те же, что и при
обработке событий от элементов управления.
83
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Кроме простых пунктов меню можно использовать пункты меню с "галочками" – подобие элементов управления-переключателей. Для этого есть
специальный класс CheckboxMenuItem. Для обработки событий от таких
пунктов меню класс, производный от Frame, должен раскрывать интерфейс
ItemListener.
Задание для самостоятельной работы
Рассмотрите более подробно классы MenuBar, MenuItem и Menu.
Сетевые приложения
Для создания сетевых приложений используются стандартные классы
пакета java.net. Рассмотрим применение некоторых из них.
Пример 1. Приложение, которое запрашивает и печатает файл по
заданному URL (строки файла нумеруются).
/* Из книги J.M. Bishop 'Java jently' */
import java.io.*;
import java.net.*;
import java.util.*;
class Lister {
public static void main(String[] args) throws IOException {
try {
// создаем объекты URL и URLConnection
URL resource = new URL(args[0]);
URLConnection c = resource.openConnection();
// печатаем имя файла и дату создания,
//а также некоторые заголовки
System.out.println(args[0]+" created "+
new Date(c.getDate()));
System.out.println("ContentType: " +c.getContentType());
System.out.println("ContentEncoding: " +
c.getContentEncoding());
System.out.println("ContentLength: " +
84
Практикум по программированию на языке Java
c.getContentLength());
System.out.println("LastModifed: " +
new Date(c.getLastModified()));
for (int i=0; i<args[0].length()+32; i++)
System.out.print('-');
System.out.println("\n");
// создаем поток вывода и печатаем все строки
BufferedReader in = new BufferedReader
(new InputStreamReader(c.getInputStream()));
for (int i=1; ; i++) {
String line = in.readLine ();
if (line == null) break;
System.out.println(i+" "+line);
}
}
catch (MalformedURLException e) {
System.out.println("Неверный URL."+
"Не забудьте, что адрес начинается "+
"с http:// или file:///");
}
}
}
Это приложение получает URL как аргумент командной строки. В
Jcreator назначить аргументы командной строки можно так: в меню следует
выбрать "Configure" – "Options...", выбрать строку “JDK Tools”, в выпадающем списке выбрать "Run application", нажать на кнопку “New” или
“Edit” и на закладке "Parameters" включить переключатель "Prompt for
main method arguments". Теперь при запуске приложения будет появляться
окошко, в котором можно задать аргументы командной строки. Для нашего
приложения требуется задать какой-нибудь адрес, например:
http://kek.ksu.ru/EOS/Java/default.html.
Теперь рассмотрим основные действия, выполняемые в программе.
Сначала создадим объект URL:
URL resource = new URL(args[0]);
85
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Затем для чтения данных создадим объект URLConnection на основе
объекта URL:
URLConnection c = resource.openConnection();
Теперь можно получить ссылку на поток ввода, связанного с данным
ресурсом, и создать свой буферизованный поток ввода:
BufferedReader in = new BufferedReader
(new InputStreamReader(c.getInputStream()));
Данные из этого потока можно читать, например, построчно, с помощью метода getLine.
Наконец, отметим, что конструктор URL может генерировать исключение MalformedURLException, а метод openConnection и методы объектов-потоков ввода-вывода могут генерировать исключение IOException.
Следовательно, эти исключения нужно обрабатывать.
В предыдущем примере мы рассмотрели, как устанавливается соединение с web-ресурсами на высоком уровне, т.е., через URL. Java предоставляет
возможность работать с соединениями и на более низком уровне – через порты и сокеты.
Порт – это абстрактный физический объект, через который осуществляется коммуникация между клиентом и сервером. При этом сервер предоставляет порт, а клиент с ним соединяется.
В операционных системах имеются процессы, назначенные конкретным портам. Программное обеспечение сервера непрерывно прослушивает
эти порты на предмет обнаружения сообщений определенного вида. Как правило, порты идентифицируются с помощью номеров. Например, известные
нам web-серверы (IIS, Apache) по умолчанию используют порт 80, CУБД
MySQL по умолчанию запускается на порте 3306, и т.п. Однако есть много
незадействованных портов, которые мы можем применять для создаваемых
нами служб.
86
Практикум по программированию на языке Java
Примером такой службы может быть многопотоковая Java-программа,
обеспечивающая сокеты для данного порта. Сокет (в переводе с английского
– гнездо, патрон) – это абстракция сетевого программного обеспечения, которая дает возможность осуществлять соединения с сетевой программой и
обмениваться с ней входными и выходными данными. Простейшим методом
организации обмена является установление обычных потоков ввода-вывода
от сервера клиенту и наоборот. Для одного порта можно создать несколько
клиентских сокетов, что позволяет нескольким пользователям получать доступ к данной службе.
Если клиентом является Java-программа, то она также должна иметь
сокет и потоки, соответствующие аналогичным потокам сервера. Клиентом
может быть и служебная программа, такая, как telnet.
Пример 2. ATM (automated teller machine) – банкомат. Эта программа имитирует соединение с банком и проверку достоверности вводимого клиентом личного идентификациоонного номера (PIN-кода). В
качестве клиентской программы используется telnet – подключение
нужно проводить к серверу localhost и порту 8190.
import java.io.*;
import java.net.*;
class ATMServer {
/* Простая серверная программа, J M Bishop Декабрь 1996
* =======================
*
Java 1.1 обновлено Январь 1998
*
Java 1.2 B Worrall Август 2000
* Позволяет выполнять много одновременных подключений.
* Иллюстрирует использование сокетов и работу в сети. */
static final String
static int
InetAddress
private boolean
magicPIN = "5678";
port = 8190;
serverAddress = null;
done = false;
public static void main(String[] args) {
// Получает номер порта из строки параметров
87
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
// или назначает значение по умолчанию 8190
if (args.length > 0)
port =Integer.parseInt(args[0]);
new ATMServer (port);
}
ATMServer (int port) {
try {
serverAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {}
// Информация при запуске сервера
System.out.println("****** Саванна банк ********");
System.out.println("Имитирует сеанс ATM "
+ "для клиентов, подключающихся через telnet к серверу ");
System.out.println(serverAddress.getHostName() +
"к порту "+port);
System.out.println("от любого количества компьютеров или ");
System.out.println("из любого количества окон.");
// Инициализирует ServerSocket
try {
ServerSocket listener = new ServerSocket(port);
int c = 0;
while (!done) {
// Создается пауза ожидания клиентских подключений
Socket client = listener.accept( );
c++;
System.out.println("Кредитная карточка вставлениа в " +
client.getInetAddress().getHostName());
System.out.println("Начало обслуживания "+
"нового клиента под номером "+c);
new handler(client, c).start();
}
listener.close();
}
catch (IOException e) {
System.out.println("Порт "+port+
", возможно, занят. Попробуйте еще раз.");
}
}
void closeDown () {
done = true;
}
class handler extends Thread {
private Socket toClient;
88
Практикум по программированию на языке Java
private int id;
handler(Socket s, int i) {
// Запоминает номер сокета и клиентский ID
toClient = s;
id = i;
}
public void run() {
try {
DataInputStream conin =
new DataInputStream (toClient.getInputStream());
PrintWriter conout = new
PrintWriter(toClient.getOutputStream(),true);
conout.println( "Welcome to Savanna bank");
for (int tries = 0; tries < 3; tries++) {
conout.println("Please, enter your PIN-code "+
"or CANCEL");
String s = conin.readLine();
System.out.println("Клиент "+id+":"+s);
if (s.equals("SHUTDOWN")) closeDown();
else if (s.equals("CANCEL")) {
conout.println("Transaction is complete. Goodbye.");
break;
}
else if (s.equals(magicPIN)) {
conout.println("You can begin a transaction");
break;
}
else
conout.println("Incorrect PIN-code."+
"Try again.");
}
System.out.println("Имитация завершена. Спасибо.");
conout.println("-------------------------");
conout.println("Imitation is complete. Thanks.");
}
catch (IOException e) {System.out.println("IO Error");}
}
}
}
Номер порта программа получает из командной строки или назначает
по умолчанию порт 8190.
В конструкторе с помощью метода getLocalHost() класса InetAddress
получаем локальный адрес (обычно это "localhost"):
89
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
serverAddress = InetAddress.getLocalHost();
Основную работу выполняет объект класса ServerSocket. Этот объект
создается на заданном порте и выполняет функции "слушателя":
ServerSocket listener = new ServerSocket(port);
При успешном создании объекта в бесконечном цикле устанавливается
режим ожидания подключений со стороны клиентов. Метод accept создает
паузу, которая заканчивается при поступлении нового подключения от очередного клиента. При этом создается новый объект класса Socket:
Socket client = listener.accept( );
Сообщения от клиентов могут поступать с различными скоростями,
поэтому для параллельной обработки в данной программе предусмотрен
класс handler, производный от класса Thread. Объект этого класса создается
и запускается на выполнение командой
new handler(client, c).start();
В методе run этого потока происходит обмен информацией с клиентом. Для этого создаются два объекта – входной и выходной поток:
DataInputStream conin =
new DataInputStream (toClient.getInputStream());
PrintWriter conout =
new PrintWriter(toClient.getOutputStream(),true);
У клиента есть три попытки набора правильного PIN-кода. Кроме того,
клиент может набрать команду CANCEL для завершения сеанса и команду
SHUTDOWN для остановки сервера (разумеется, последняя возможность не
слишком правдоподобна).
90
Практикум по программированию на языке Java
Таким образом, в данном примере рассмотрена только авторизация
клиента. Получение информации о счете, возможность снятия денег со счета
и т.п. не предусмотрены.
Пример 3. Простейший клиент. Он подключается к порту 8190 хоста localhost, принимает одно сообщение (строку) и отправляет одно сообщение (строку), и на этом прекращает работу.
Основные действия, которые выполняются в этой программе:
1) создание клиентского сокета
Socket mySock = new Socket("localhost",8190);
2) создание входного потока для сокета
BufferedReader in =
new BufferedReader(
new InputStreamReader(mySock.getInputStream()));
3) создание выходного потока для сокета
PrintWriter out =
new PrintWriter(mySock.getOutputStream(), true);
4) чтение строки из входного потока
str=in.readLine();
5) запись строки в выходной поток
out.println("Done");
Задание для самостоятельной работы
1. Рассмотрите более подробно классы Socket и ServerSocket.
91
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
2. Рассмотрите самостоятельно пример реализации чат-сервера. Чатсервер – это программа, которая запущена на некотором порте некоторого
хоста и находится в режиме ожидания подключений со стороны клиентов.
Клиент может подключиться к серверу и посылать сообщения, которые сервер перенаправляет всем клиентам. Код программы приведен в примере 3
приложения 5.
92
Практикум по программированию на языке Java
Глава 5. Немного о сервлетах и Java Server Pages
Сервлеты и JSP представляют собой Java-технологии, предназначенные для разработки серверных web-сценариев.
Сервлеты - это программы, которые выполняются на web-сервере,
действуя в качестве посредника между запросом, поступающим от webбраузера или другого HTTP-клиента, и приложениями на HTTP-сервере (базами данных и др.)
Технология Java Server Pages позволяет смешивать обычный, статический HTML-код с динамически сгенерированным содержимым сервлетов
(подобно технологиям PHP и ASP).
Что нужно для разработки сервлетов и JSP?
Для работы с сервлетами и JSP требуется установить web-сервер, поддерживающий Java. Некоторые серверы специально разработаны c поддержкой Java, другие позволяют устанавливать для этой цели дополнительные
модули (plug-ins).
Рассмотрим известный web-сервер Apache Tomcat. Для его установки
нужно просто распаковать архив в некоторый каталог, например, C:\TomCat.
По умолчанию этот сервер использует нестандартный порт 8080, поэтому
для просмотра страниц из браузера нужно использовать адрес
http://localhost:8080
Для запуска сервера используется пакетный файл startup.bat из каталога BIN.
Сервер TomCat работает на Java-платформе, поэтому на компьютере
также должен быть установлен JDK. Чтобы указать серверу, где именно
93
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
установлен JDK, в файле startup.bat нужно инициализировать переменную
JAVA_HOME,
например,
так
(если
JDK
установлен
в
каталог
C:\j2sdk1.4.0):
set JAVA_HOME=C:\j2sdk1.4.0
Если вы работаете в ОС Win98, то, вероятно, вам потребуется изменить
объем памяти для переменных среды. Запустите окно DOS, щелкните на пиктограмме "Параметры" и выберите закладку "Память". Измените значение
параметра "Переменные среды" c Auto на 2816. Эта настройка производится
только один раз.
Корневым каталогом для TomCat является
C:\TOMCAT\WEBAPPS\ROOT
Наберите в адресной строке браузера
http://localhost:8080
Браузер загрузит страницу index.html из этого каталога.
Пример сервлета
Рассмотрим любимый пример "Hello, World!".
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n";
out.println(docType +
94
Практикум по программированию на языке Java
"<HTML>\n" +
"<HEAD><TITLE>Hello, World! "+
"</TITLE></HEAD>\n" +
"<BODY>\n" +
"<H1>Hello, World!</H1>\n" +
"</BODY></HTML>");
}
}
Сначала в этом примере импортируются дополнительные библиотеки
классов для сервлетов. Эти библиотеки находятся в архиве servlet.jar, расположенном в каталоге LIB сервера TomCat. Для подключения этого архива
используйте настройку Classpath вашего проекта, если вы работаете в JCreator, или установку Classpath в переменных среды.
Любой сервлет является производным от класса HttpServlet. Обычно в
нем переопределяется метод doGet, или doPost, или оба эти метода (можно
переопределить один из них и вызвать его из другого). Эти методы могут получать параметры запроса (с помощью своего первого параметра – объекта
request), а затем формируют ответ сервера (с помощью второго параметра –
объекта response).
Первой строкой ответа должно быть формирование заголовка ContentType:
response.setContentType("text/html");
Затем получаем ссылку на выходной поток сервлета:
PrintWriter out = response.getWriter();
и выводим любую информацию в этот выходной поток (в данном случае простой HTML-код).
Теперь этот сервлет нужно откомпилировать. Полученный файл
HelloWorld.class следует поместить в специальный каталог сервера TomCat
для сервлетов, в нашей версии это
C:\TOMCAT\WEBAPPS\ROOT\WEB-INF\CLASSES
95
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Для запуска сервлета в адресной строке браузера наберите
http://localhost:8080/servlet/HelloWorld
В том случае, когда сервлету передаются параметры, получить их
можно с помощью метода getParameter объекта request типа HttpServletRequest. Например, для получения параметра с именем klient можно
использовать следующую команду:
String customer=request.getParameter("klient");
Задание для самостоятельной работы
Рассмотрите самостоятельно более сложный пример, который касается
передачи параметров сервлету из формы. Он состоит из двух файлов: HTMLформы, передающей параметры сервлету и сервлета, получающего параметры из HTML-формы. Этот пример под номером 4 приведен в приложении 5.
Пример JSP
Простой пример JSP-сценария получает название цвета фона в качестве параметра и формирует страницу с заданным цветом фона.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Color Testing</TITLE>
</HEAD>
<%
String bgColor = request.getParameter("bgColor");
boolean hasExplicitColor;
if (bgColor != null) {
hasExplicitColor = true;
} else {
hasExplicitColor = false;
bgColor = "WHITE";
}
96
Практикум по программированию на языке Java
%>
<BODY BGCOLOR="<%= bgColor %>">
<H2 ALIGN="CENTER">Color Testing</H2>
<%
if (hasExplicitColor) {
out.println("Вы явно задали цвет фона: " +
bgColor + ".");
} else {
out.println("Цвет фона по умолчанию: WHITE." +
"Задавайте значения параметру bgColor " +
"в виде имени цвета, или в формате RRGGBB");
}
%>
</BODY>
</HTML>
Как видим, в JSP-сценарии могут быть перемешаны HTML-теги и операторы языка Java. Серверные команды следует помещать в теги <% %>, как
в ASP.
В серверном коде используются те же самые объекты response и
request, как и в сервлетах.
И вообще, на основе JSP-страницы Java-сервер в любом случае (невидимо для нас) создает и выполняет сервлет.
Для тестирования нужно сохранить этот сценарий с расширением .jsp в
корневом каталоге TomCat и запускать, например, так:
http://localhost:8080/BGColor.jsp?bgColor=lightblue
Если в JSP-сценарии требуется подключение к серверу баз данных
(например, к MySQL), то в начале JSP-страницы следует импортировать
нужные пакеты:
<%@ page import="java.sql.*" %>
<%@ page import="com.mysql.jdbc.Driver" %>
Соответственно, для работы с файлами в начале JSP-страницы следует
импортировать пакет:
97
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
<%@ page import="java.io.*" %>
Передача файла из HTML-формы сервлету
Иногда требуется отправлять данные в виде файла сервлету (или JSPсценарию) на обработку. В этом случае HTML-форма, отправляющая данные, должна иметь атрибут
enctype="multipart/form-data"
Форма должна иметь поле типа file для выбора файла из файловой системы локального компьютера.
Получение данных, которые передаются сервлету, можно произвести с
помощью одного из двух видов потоков – байтового (двоичного) или символьного.
Байтовый поток имеет тип ServletInputStream, и ссылку на него можно получить с помощью метода getInputStream объекта request. Для чтения
из этого потока используется метод со следующим прототипом:
int readLine(byte[] buf, int offset, int length)
Этот метод позволяет читать в buf данные из входного потока, начиная
с байта с номером offset длиной length. Метод возвращает число прочитанных байт из входного потока или -1, если входной поток уже исчерпан.
Символьный поток имеет тип BufferedReader и ссылку на него можно
получить с помощью метода getReader объекта request.
Заметим, что данные из HTML-формы передаются в сопровождении
HTTP-заголовков. Например, если форма содержит текстовое поле с именем
klient и поле передачи файла myfile, передаваемые данные могли быть такими:
-----------------7d62711f901f81
Content-Disposition: form-data; name="klient"
Значение, введенное в текстовое поле
98
Практикум по программированию на языке Java
-----------------7d62711f901f81
Content-Disposition: form-data; name="myfile";
filename="локальные путь и имя передаваемого файла"
Content-Type: text/plain
Содержимое файла
-----------------7d62711f901f81
Последовательность символов "-----------------7d62711f901f81"
пред-
ставляет собой так называемую границу (для каждого запроса такая последовательность генерируется заново). Эта граница разделяет текст передаваемых
данных на части. Естественно, что для правильной трактовки содержимого
передаваемых данных требуется разделить извлекаемые из потока заголовки
и данные.
Разумеется, вручную анализировать данные потоков довольно сложно.
К счастью, существуют разные пакеты классов, использование которых позволяет сделать этот анализ более высокоуровневым. Рассмотрим пример
применения одного из таких пакетов – com.ibm.useful.http.
import
import
import
import
javax.servlet.*;
javax.servlet.http.*;
java.io.*;
com.ibm.useful.http.*;
public class ReceiverServlet
extends javax.servlet.http.HttpServlet
{
public void doPost(javax.servlet.http.HttpServletRequest req,
javax.servlet.http.HttpServletResponse res)
throws
javax.servlet.ServletException,
java.io.IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String docType = "<!DOCTYPE HTML PUBLIC \"-//W3C//"+
"DTD HTML 4.0 Transitional//EN\">\n";
out.println(docType + "<HTML>\n" +
"<HEAD><TITLE>File upload</TITLE></HEAD>\n"+
"<BODY bgcolor='#ccdddff'>\n");
try
{
//проверка, пришел ли запрос в multipart формате
if(isMultipartFormat(req))
99
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
{
//разбор формата multipart и помещение информации
//из запроса в поля объекта класса PostData
PostData multidata=new PostData(req);
//извлечение посланной информации
String fileDescription=
multidata.getParameter("klient");
com.ibm.useful.http.FileData tempFile=
multidata.getFileData("myfile");
if (tempFile!=null)
{
saveFile(tempFile);
out.println("<H1>Done</H1>\n");
}
}
}
catch(Exception e)
{ System.out.println(e.toString());
out.println("</BODY></HTML>");
out.close();
}
}
//функция, проверяющая, пришел ли запрос в формате multipart
private boolean isMultipartFormat(HttpServletRequest req)
throws javax.servlet.ServletException,
java.io.IOException
{
String temptype=req.getContentType();
if(temptype.indexOf("multipart/form-data")!=-1)
return true;
else return false;
}
// функция, сохраняющая пришедший файл
// на диск в каталог "c:\temp"
private void saveFile(com.ibm.useful.http.FileData tempFile)
throws IOException
{
java.io.File f = new java.io.File
("c:\\temp\\" + tempFile.getFileName());
FileOutputStream fos = new FileOutputStream(f);
fos.write(tempFile.getByteData());
fos.close();
}
}
Апплет как интерфейс к сервлету
Альтернативой передачи данных сервлету с помощью HTML-форм является использование для этих целей апплетов. Этот подход позволяет разра100
Практикум по программированию на языке Java
батывать более сложный пользовательский интерфейс для представления
вводимых данных. Также неоспоримым преимуществом такого подхода является использование одной и той же технологии программирования для разработки и серверной части системы (сервлеты), и клиентской части (апплеты). Для такой передачи данных используется объект класса URL.
Известно, что данные HTTP-запроса могут передаваться по сети с помощью двух методов передачи данных – GET и POST. При передаче параметров методом GET данные передаются в адресной строке. В этом случае
схема вызова сервлета и передачи данных в него выглядит так:
// пусть имена параметров и их значения хранятся в переменных
// name1, name2 – имена параметров,
// val1, val2 – значения параметров
String someData=name1+"="+val1+"&"+name2+"="+val2;
try
{
//создание URL на сервлет
URL servURL=new URL("servlet/serv1"+"?"+someData);
// загрузка сервлета
getAppletContext().showDocument(servURL);
}
catch(MalformedURLException e)
{
}
Получение данных сервлетом при такой передаче параметров осуществляется так же, как если бы они передавались из HTML-формы.
Интересным является случай, когда апплет не перестает работать после передачи данных сервлету, а принимает от сервлета некоторый ответ и
продолжает работу далее. Получается, что сервлет срабатывает "на втором
плане", принимая данные от апплета и обрабатывая их. Это можно сделать
только с помощью метода POST.
Для передачи данных сервлету методом POST и для получения ответа
апплет должен выполнять следующие действия:
1. Создать объект класса URL, содержащий ссылку на домашний хост
апплета:
101
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
URL currentPage=getCodeBase();
String protocol=currentPage.getProtocol();
String host=currentPage.getHost();
int port=currentPage.getPort();
URL dataURL=new URL(protocol,host,port,"/servlet/myServlet");
2. Создать объект класса URLConnection для получения входных и
выходных потоков апплета:
URLConnection conn=dataURL.openConnection();
3. Дать браузеру указание не кэшировать результаты.
conn.setUseCaches(false);
4. Сообщить системе, что апплет будет не только читать, но и посылать
данные:
conn.setDoOutput(true);
5. Создать объект класса ByteOutputStream для буферизации данных,
которые посылаются сервлету (для дальнейшего определения размера посылаемых данных):
ByteArrayOutputStream byteStream=new ByteArrayOutputStream(100);
6. Присоединить выходной поток к объекту класса ByteOutputStream:
PrintWriter out=new PrintWriter(byteStream,true);
7. Поместить данные в буфер:
String someData=name1+"="+val1+"&"+name2+"="+val2;
out.print(someData);
out.flush();
102
Практикум по программированию на языке Java
8. Задать заголовок Content-Length (обязателен при передаче параметров методом POST):
conn.setRequestProperty("Content-Length",
String.valueOf(byteStream.size()));
9. Задать заголовок Content-Type.
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
10. Послать данные:
byteStream.writeTo(conn.getOutputStream());
11. Открыть входной поток для получения ответа:
BufferedReader in=new BufferedReader(new
InputStreamReader(conn.getInputStream()));
12. Прочитать результат:
String line;
while((line=in.readLine())!=null)
// какие-то действия с полученным ответом
Сервлет в этом случае должен принимать и обрабатывать данные таким же образом, что и в случае приема символьных данных от HTML-формы.
Приведем пример взаимодействия апплета и сервлета. Апплет отсылает
серверу информацию, вводимую в текстовое поле, а сервлет записывает информацию в заданный файл, после чего возвращает строку о том, что операция прошла успешно.
// апплет, из которого передаются данные
import java.io.*;
import java.applet.*;
103
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
import java.awt.*;
import java.awt.event.*;
import java.net.*;
public class App extends Applet implements ActionListener
{
// записывается посылаемая информация
TextField info;
// кнопка отсылки данных
Button submitButton;
// область отображения ответа сервлета
TextArea result;
public void init()
{
add(new Label("Введите информацию"));
info =new TextField(50);
add(info);
submitButton=new Button("Отослать");
submitButton.addActionListener(this);
add(submitButton);
result=new TextArea(20,50);
add(result);
}
public void actionPerformed(ActionEvent e)
{
if(submitButton==e.getSource())
{
try
{
URL currentPage=getCodeBase();
String protocol=currentPage.getProtocol();
String host=currentPage.getHost();
int port=currentPage.getPort();
String urlStr="/servlet/MyServ";
URL dataURL=new
URL(protocol,host,port,urlStr);
URLConnection conn=dataURL.openConnection();
conn.setUseCaches(false);
conn.setDoOutput(true);
ByteArrayOutputStream byteStream=new
ByteArrayOutputStream(100);
PrintWriter out=new
PrintWriter(byteStream,true);
out.print(info.getText());
out.flush();
conn.setRequestProperty("Content-Length",
String.valueOf(byteStream.size()));
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
byteStream.writeTo(conn.getOutputStream());
104
Практикум по программированию на языке Java
BufferedReader in=new BufferedReader(new
InputStreamReader(conn.getInputStream()));
String line;
result.setText("");
while((line=in.readLine())!=null)
result.append(line+"\n");
}
catch(Exception ee){}
}
}
}
// сервлет, получающий данные и записывающий их в файл
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class MyServ extends HttpServlet
{
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html;
charset=windows-1251");
PrintWriter out=response.getWriter();
BufferedReader in=request.getReader();
FileWriter file=new FileWriter("c://data.txt");
String s;
while((s=in.readLine())!=null)
file.write(s);
file.close();
out.println("Ваша информация записана");
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
doPost(request, response);
}
}
Использование сервлетов для генерации GIF-изображений
Задача генерации изображений возникает достаточно часто в случае
построения динамических рисунков, зависящих от тех или иных условий.
Информация для построения картинок может выбираться из базы данных
105
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
или задаваться пользователем с помощью HTML-форм. Типичным примером
такой задачи является построение диаграмм.
Средствами языка Java можно создать изображение в рабочей области
апплета (см. главу 3). Однако этот способ имеет один недостаток (общий для
всех клиентских технологий) – зависимость от версии и настроек браузера.
Есть только один способ преодолеть этот недостаток – возложить функции,
выполняемые на стороне клиента, на сервер. Этот способ имеет и очевидное
преимущество – генерация осуществляется в одном из стандартных форматов изображений, которые "понимают" все широко используемые браузеры
(например, в формате gif).
Для создания изображения и отправки его в том или ином графическом
формате в выходной поток разработаны различные пакеты классов. Мы будем использовать один из наиболее известных классов – GifEncoder (его автор Джеф Посканзер), который находится в пакете Acme (его можно найти
на сайте http://www.acme.com).
Рассмотрим сервлет, генерирующий и отсылающий клиенту изображение.
// Пример сервлета, генерирующего gif-изображение "Колобок"
import
import
import
import
import
java.io.*;
javax.servlet.*;
javax.servlet.http.*;
java.awt.*;
Acme.JPM.Encoders.GifEncoder;
public class Kartinki extends HttpServlet
{
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// Создадим системный объект, играющий роль окна
// (поскольку сервер никаких окон не открывает)
Frame f=new Frame();
f.addNotify();
// Зададим параметры "окна"
int width=700;
int height=330;
106
Практикум по программированию на языке Java
// Создадим объект класса Image
Image img=f.createImage(width,height);
// Создадим объект канвы для работы с графикой
Graphics g=img.getGraphics();
// Рисуем в этом объекте, все, что захотим
// Например, рисуем колобка
g.setColor(Color.yellow);
g.fillOval(300,70,150,150); //лицо
g.setColor(Color.black);
g.fillOval(340,110,15,15); //левый глаз
g.fillOval(400,110,15,15); //правый глаз
g.drawArc(325,100,100,100,0,-180); // улыбка
// Укажем MIME-тип содержимого динамически
// создаваемого документа
response.setContentType("image/gif");
// Получим доступ к выходному потоку
// Так как мы выводим бинарные данные, используем
// метод getOutputStream класса HttpServletResponse
OutputStream out1=response.getOutputStream();
// Отправим наш объект класса Image
// в gif-формате в выходной поток.
// Изображение и поток вывода передаются в качесте
// параметров в конструктор класса GifEncoder
GifEncoder encoder;
encoder = new GifEncoder(img, out1);
encoder.encode();
}
}
В результате получится следующее изображение:
107
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Практические задания
В рамках практических занятий нужно выполнить 7 заданий на определенные темы. Для каждой темы приведено "типовое задание". Вы можете
выполнять именно его, или же придумать собственный пример на данную
тему (собственные примеры необходимо предварительно обсудить с преподавателем).
Задание 1
Создание апплета с графической информацией, передача параметров из HTML-страницы.
Первое задание состоит в разработке апплета, в котором с помощью
функций рисования создано некоторое изображение. Кроме того, апплет
должен получать и использовать параметры, переданные из HTMLстраницы.
Типовое задание. Предположим, что для вашего электронного магазина требуется вывод различной информации в виде диаграмм - например, объем продаж за текущий год по месяцам, спрос на разные виды товаров и т.п.
Разработайте апплет, который получает числовые данные из HTMLстраницы (например, 12 чисел, представляющих собой объем продаж за 12
месяцев текущего года) и рисует диаграмму (диаграммы бывают разных видов: гистограммы, круговые, линейные, точечные, с областями, объемные и
т.п.) Ясно, что саму HTML-страницу с данными для апплета можно создавать
динамически – можно, например, с помощью ASP- или PHP-сценария выбрать информацию из базы данных (в данном задании это делать необязательно).
108
Практикум по программированию на языке Java
Какие темы нужно изучить, чтобы выполнить это задание?
Для выполнения данного задания необходимо изучить:
 все темы главы 1 "Основы языка Java",
 а также параграфы главы 3
o "Работа с графикой",
o "Передача параметров в апплет".
Кроме того, глава 2 необходима для понимания объектно-ориентированных
принципов Java.
Задание 2
Создание апплета с элементами управления, обработка событий
Второе задание состоит в разработке апплета, содержащего, кроме
изображения, элементы управления (текстовые поля, радиокнопки, флажки,
списки и т.п.). Апплет должен обрабатывать события, поступающие от этих
элементов управления и соответствующим образом изменять изображение.
Типовое задание. Рассмотрим тот же самый пример, что и в предыдущем задании – построение диаграммы. Предположим, что вам нужно предусмотреть настройку изображения – цвет диаграммы, c, тип диаграммы (гистограмма, круговая, линейная, точечная, с областями, объемная и т.п.). В
этом случае элементы управления можно разместить непосредственно в апплете. При изменении значений элементов управления апплет должен перерисовывать изображение.
Какие темы нужно изучить, чтобы выполнить это задание?
Кроме главы 1 "Основы языка Java", для выполнения данного задания необходимо изучить тему главы 3 "AWT и обработка событий". Кроме того, глава 2 необходима для понимания объектно-ориентированных
принципов Java.
109
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Задание 3
Создание апплета с использованием потоков
Третье задание состоит в разработке апплета, в котором используются
потоки (thread). Потоки удобно применять, например, для создания анимированного изображения.
Типовое задание. Один из примеров анимированного изображения –
фейерверки. Вид фейерверка может быть любым, главное – он должен представлять собой периодически изменяющееся изображение.
Какие темы нужно изучить, чтобы выполнить это задание?
Кроме главы 1 "Основы языка Java", для выполнения данного задания необходимо изучить тему главы 3 "Работа с потоками". Кроме того,
глава 2 необходима для понимания объектно-ориентированных принципов
Java.
Задание 4
Создание приложения для работы с файлами
Четвертое задание состоит в разработке приложения (можно консольного), в котором предусмотрены запись/чтение данных в файл/из файла.
Можно использовать классы для последовательного или произвольного доступа к файлам, класс для получения информации о файлах и каталогах, канальные потоки ввода/вывода и т.п.
Типовое задание. В качестве темы задания можно выбрать практически любой пример работы с файлами – подсчет слов в текстовом файле,
сравнение двух файлов и печать несовпадающих строк и т.п.
110
Практикум по программированию на языке Java
Какие темы нужно изучить, чтобы выполнить это задание?
Поскольку это первое задание, связанное с программой-приложением,
для его выполнения необходимо рассмотреть параграф главы 4 "Создание
первого приложения". О том, как работать с файлами и прочими потоками
ввода/вывода, можно прочитать в параграфе "Работа с файлами". При работе с файлами не обойтись без исключений – параграф "Работа с исключениями".
Задание 5.
Создание приложения для работы с базой данных
Пятое задание заключается в создании простого приложения (консольного или оконного) для работы с базой данных. В качестве СУБД предлагается использовать MySQL.
Типовое задание. Оконное приложение для работы с базой данных
может содержать выполнение какой-либо SQL-команды и просмотр ее результатов. Консольное приложение должно содержать несколько SQLкоманд.
Какие темы нужно изучить, чтобы выполнить это задание?
О том, как подключаться к базам данных, можно прочитать в параграфе "Работа с базами данных". Если в качестве интерфейса для работы с базой данных вы не хотите использовать консольное приложение, то изучите
параграф "Создание оконных приложений".
111
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Задание 6
Создание сетевого приложения
Шестое задание состоит в разработке приложения с архитектурой клиент-сервер, использующего серверные и/или клиентские сокеты. Вы можете
разрабатывать только серверную часть какого-либо приложения (в качестве
клиентской части можно использовать telnet), или только клиентскую часть
как дополнение к серверной части примеров, рассмотренных в учебнике
(ATM-сервер и чат-сервер). В качестве клиентской программы можно использовать апплет. Разумеется, вы можете разрабатывать и серверную, и
клиентскую часть своего приложения.
Типовое задание. В качестве примера клиентской части сетевого приложения вы можете разработать клиентский оконный интерфейс для банкомата или чата.
Сетевое приложение может представлять собой какую-либо автоматизированную систему, содержащую сервер и несколько клиентов, например:
 оранжерея имеет центральный компьютер (сервер) и периферийные
устройства: термометр, обогреватель, увлажнитель и т.п. При изменении
условий периферийные устройства посылают сообщения серверу, который, при необходимости, посылает сообщения другим периферийным
устройствам;
 квартира оборудована бытовой техникой, которая управляется центральным компьютером (сервером). Устройства (система отопления, микроволновая печь, холодильник и т.п.) могут получать и выполнять сообщения от
сервера. Можно предусмотреть также клиентский интерфейс для хозяина
квартиры, чтобы он мог отправлять свои указания серверу для удаленного
управления квартирой;
 атомная электростанция... ну это уж, пожалуй, слишком;
112
Практикум по программированию на языке Java
 любая клиент-серверная система, которую вы придумаете.
Какие темы нужно изучить, чтобы выполнить это задание?
О том, как создавать и использовать приложения с архитектурой клиент-сервер, говорится в параграфе "Сетевые приложения". Если в качестве
интерфейса для работы сетевого приложения вы хотите использовать окна и
меню, то изучите параграф "Создание оконных приложений".
Задание 7
Создание сервлетов.
Седьмое задание состоит в разработке одного или нескольких сервлетов, которые осуществляют обмен информацией между браузером и webсервером и обращаются к внешним источникам данных (файлам или базам
данных).
Типовое задание может состоять в разработке web-интерфейса для базы данных. Можно использовать ту же базу данных, что и в пятом задании.
Какие темы нужно изучить, чтобы выполнить это задание?
Для разработки сервлетов необходимо установить и настроить webсервер, поддерживающий Java. Работа с web-сервером TomCat, так же, как и
принципы разработки сервлетов, приведены в главе 5 "Немного о сервлетах
и Java Server Page".
113
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Приложение 1. ЧАсто задаваемые ВОпросы
Вопрос Есть ли в Java глобальные переменные?
Ответ В Java глобальных переменных нет. Ближе всего по свойствам к глобальным переменным статические переменные класса. К ним обращаются
через имя класса, например, Math.PI.
Вопрос Можно ли использовать внутренние классы в Java?
Ответ Можно. При этом после компиляции имя файла внутреннего класса
будет иметь вид: ВнешнийКласс$ВнутреннийКласс.class.
Вопрос Как в Java передаются параметры в функцию - по значению, по указателю или по ссылке?
Ответ Параметры стандартных типов передаются по значению. Объекты - по
ссылке. Передачи по указателю в Java нет.
Вопрос Есть ли в Java указатели?
Ответ Нет.
Вопрос Если в Java нет указателей, то почему иногда генерируется исключение NullPointerException?
Ответ Указатели в Java есть "на нижнем уровне" – на уровне виртуальной
машины.
Для
программистов
доступа
к
ним
нет.
Исключение
NullPointerException генерируется в том случае, если происходит обращение
к несуществующему объекту (т.е., переменную объявили, а объект не создали). Например:
String s;
int n=s.length();
114
// ошибка
Практикум по программированию на языке Java
Вопрос Почему в языке Java, имеющем операцию выделения памяти new, не
существует операции освобождения памяти delete, как, например, в языке
С++?
Ответ В языке Java программист освобожден от необходимости самостоятельно управлять памятью. Для хранения объектов здесь отводится определенное место памяти, называемое кучей. Размер кучи ограничен. Виртуальная машина Java осуществляет автоматическую "сборку мусора" – процесс
возвращения системе памяти, занятой объектами, которые больше не потребуются, и подготовки ее для использования новыми объектами. Не существует способа вызова сборщика мусора. Программист может только отправить
запрос на запуск сборки мусора с помощью метода gc объекта System. Собственно сборка мусора произойдет в момент, удобный системе.
Вопрос Можно ли в Java переопределять операции +,-,*,% и т.п.?
Ответ Нельзя. Исключение сделано только для операции +, которая определена для класса String и представляет собой операцию сцепления строк.
Вопрос Как можно вывести в апплет картинку из графического файла?
Ответ Для работы с графическими файлами используется класс Image.
 Во-первых, нужно объявить переменную:
Image pict;
 во-вторых, загрузить картинку из файла:
pict=getImage(getCodeBase(),"example.gif");
 в-третьих, вывести картинку на экран с помощью графического контекста:
gr.drawImage(pict,80,90,this);
Вопрос Как можно вывести в оконное приложение картинку из графического файла?
Ответ Это производится с помощью класса Image, как и в случае апплета.
Особенность состоит в том, что в отличие от класса Applet, в котором реали115
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
зован метод getImage, позволяющий загрузить изображение из заданного
файла, классы Frame или Window таких методов не имеют. Эту задачу можно решить с помощью объекта класса Toolkit (инструментальное окружение).
Toolkit toolkit=Toolkit.getDefaultTookit();
После этого можно загружать изображение из заданного файла, например,
так:
Image im=toolkit.getImage("example.gif");
Полученный объект im теперь можно загружать в канву для дальнейшего
помещения в окно. Кстати, при размещении канвы с использованием менеджера раскладок BorderLayout канва должна быть размещена в центре. В
противном случае канва не будет видна в окне.
Вопрос Как избежать "мелькания" при частой перерисовке изображения в
апплете?
Ответ В этом случае часто применяется технология "виртуального окна".
Для этих целей используется класс BufferedImage, который позволяет в оперативной памяти создать "виртуальное окно", куда происходит весь вывод
информации. В заданные моменты изображение должно копироваться в реальное окно. Таким образом, последовательность работы будет такова:
 Подключаем нужный пакет
import java.awt.image.BufferedImage;
 В класс-апплет вводим новые переменные:
BufferedImage image;
// виртуальное окно
Graphics imageGr;
// совместимый графический контекст
 В методе init апплета требуется создать виртуальное окно и совместимый графический контекст:
image=new BufferedImage(width, height,
BufferedImage.TYPE_BYTE_INDEXED);
imageGr=image.getGraphics();

Далее весь вывод производится в контекст imageGr, например,
imageGr.setColor(Color.red);
116
Практикум по программированию на языке Java
imageGr.fillRect(100,100,200,200);
 В момент, когда потребуется перенести содержимое виртуального окна
в реальное окно, вызывается следующая функция для контекста реального окна в методе paint апплета
public void paint(Graphics g)
{
// ...
g.drawImage(image,0,0, width, height,null);
}
Вопрос: Как сделать так, чтобы сервлеты и JSP-сценарии корректно генерировали HTML-код, содержащий русские буквы?
Ответ В сервлете требуется установить кодировку отсылаемых клиенту данных:
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html;charset=windows-1251");
// генерирование ответа
}
В случае работы с JSP-сценарием требуется включить в сценарий следующую строку:
<%@ page contentType="text/html; charset=windows-1251" %>
Вопрос: Как сделать так, чтобы в сервлеты и JSP-сценарии корректно передавались русские буквы из HTML-формы?
Ответ Параметры из HTML-формы обычно передаются в кодировке
"ISO-8859-1". Поэтому после получения параметра его нужно перекодиро117
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
вать, например, в кодировку "Cp1251". Такую перекодировку выполняет
один из конструкторов класса String, например:
String s=request.getParameter("klient");
String klient=new String(s.getBytes("ISO-8859-1"),"Cp1251");
Некоторые наиболее распространенные ошибки
Не забывайте, что при создании апплета класс, производный от Applet, должен иметь тип доступа public и имя, совпадающее с именем файла.
Не забывайте явно создавать объекты командой new. В некоторых случаях
объекты создаются неявно – например, при объявлении строковой переменной с инициализацией:
String s = "Изучаем Java";
или при вызове некоторой функции, которая возвращает созданный ею объект:
Graphics g = getGraphics();
Во всех остальных случаях необходимо использовать команду new. Обращение к несуществующему объекту вызовет исключение NullPointerException.
Не забывайте в случае использования нестандартных пакетов явно или неявно подключать их к JDK. Существуют два подхода:
 пакет нужно поместить в каталог расширений JRE, т.е., в
C:\j2sdk1.4.0\jre\lib\ext;
 или же этот файл можно поместить в любой каталог и подключить в
свойствах JCreator. Выберите меню "Configure" – "Options...", затем строку “JDK Profiles”, выберите профайл и нажмите кнопку “Edit”, затем в появившемся окне добавьте нужный архив или каталог.
118
Практикум по программированию на языке Java
Приложение 2. Как работать с JCreator
JCreator является достаточно простой и удобной (если привыкнуть)
графической оболочкой для JDK. Для работы с JCreator будут полезны следующие советы.
 При установке JCreator попросит настроить путь к корневому каталогу
JDK. Те же самые действия можно произвести в любое время вручную,
если выбрать пункт меню "Configure" – "Options...", выбрать строку
“JDK Profiles” и создать новый профиль, задав нужный каталог. Имя
корневого каталога Java может выглядеть примерно как "C:\j2sdk1.4.0".
 Работу в JCreator лучше всего начинать с создания проекта: "Project" –
"New project". Для создания апплета выбирайте шаблон “Basic Java applet”, для создания консольного приложения – “Empty project”, для создания оконного приложения – “Basic Java application” или “Basic JFC
application”. Для каждого проекта автоматически создается отдельная
папка на диске. Файлы проектов имеют расширение .JCP. Если было открыто рабочее пространство, то проект добавляется в него, если рабочего
пространства не было, то оно создается с тем же именем, что и проект.
 В левой части экрана в окне рабочего пространства выдается список проектов. В этом списке только один проект может быть открытым – он выделен жирным шрифтом. Именно открытый проект запускается на выполнение. Для того чтобы открыть проект, щелкните по нему правой
кнопкой мыши и выберите пункт "Set as active project".
 При подключении уже существующего файла, который находится за пределами папки проекта, JCreator спросит, как этот файл подключать. Если
119
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
вы нажмете кнопку “Add”, то файл будет скопирован в папку проекта, если кнопку “External file” – то он будет подключен как внешний файл.
 Если в окне редактирования не читается русский шрифт, то выберите
пункт меню "Configure" – "Options...", в списке слева выберите “Editor”
– “Java” (или другой тип файлов) – “Font” и назначьте шрифт, который
поддерживает кириллицу.
 После того, как вы наберете текст программы в файле с расширением
.java, его нужно откомпилировать с помощью меню "Build" – "Compile
file" или “Compile project” (или кнопки с вертикальной стрелкой, или
клавиши F7). Если есть ошибки, они будут выведены в нижнем окне.
 Для запуска апплета или приложения на выполнение используйте меню
"Build" – "Execute project" (или кнопку с горизонтальной стрелкой, или
клавишу F5).
 В JCreator назначить аргументы командной строки можно так: в меню
следует выбрать "Configure" – "Options...", выбрать строку “JDK
Tools”, в выпадающем списке выбрать
"Run application", нажать на
кнопку “New” или “Edit” и на закладке "Parameters" включить переключатель "Prompt for main method arguments". Теперь при запуске приложения будет появляться окошко, в котором можно задать аргументы командной строки.
 Для подключения в проект нестандартных пакетов (например, мы можем
столкнуться с этим, когда нужно подключить JDBC-пакет для MySQL)
следует снова отредактировать профиль. Выберите меню "Configure" –
"Options...", затем строку “JDK Profiles”, выберите профиль и нажмите
120
Практикум по программированию на языке Java
кнопку “Edit”, затем в появившемся окне добавьте нужный архив или каталог.
 JCreator имеет встроенный браузер для просмотра HTML-страниц. Для
его вызова щелкните правой кнопкой мыши на каком-либо HTML-файле и
выберите пункт “View in browser” (следует заметить, что этот браузер не
всегда корректно работает).
121
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Приложение 3. Введение в настраиваемые типы данных
Новая версия JDK – Java 2 Platform Standard Edition Version 5.0 –
предоставляет программисту некоторый набор новых возможностей. Одной
из таких возможностей являются настраиваемые типы данных. Программисты, которые работают на языке С++, привыкли использовать шаблоны классов и функций. Эта же возможность в Java 2 версии 5.0 называется созданием настраиваемого типа. С помощью таких типов данных можно разработать
универсальный класс, автоматически обрабатывающий данные различных
типов. Таким образом, разрабатывается один алгоритм обработки данных,
который затем применяется без дополнительной доработки к различным типам данных.
Разберем принципы создания и использования настраиваемого класса
на примере. Создаем настраиваемый класс.
class Gen<T>
{
T ob;
public Gen (T o) {ob=o;}
public T getOb() {return ob;}
public void showType(Graphics g, int x, int y)
{
g.setColor(Color.green);
g.drawString("Тип T "+ob.getClass().getName(),x,y);
}
}
Здесь Т – параметр типа. Это имя используется как заменитель действительного типа, передаваемого в класс Gen в качестве параметра. Далее
имя Т будет использоваться в классе везде, где требуется переменная или параметр этого типа. Например, он используется для объявления переменной
класса, в качестве параметра в конструкторе класса, а также как тип возвращаемого значения в методе getOb. Если при создании объекта будет передан
122
Практикум по программированию на языке Java
тип String, то у переменной ob будет тип String. Таким образом, реальный
тип данных определится только при создании объекта.
Создадим класс, который будет являться внешним для созданного
настраиваемого класса Gen и будет демонстрировать работу с этим внутренним классом.
public class GenDemo extends Applet
{
Gen<Integer> iOb;
Gen<String> strOb;
public void init()
{
int i=Integer.parseInt(getParameter("intParam"));
iOb=new Gen<Integer>(i);
String s=getParameter("strParam");
strOb=new Gen<String>(s);
}
public void paint(Graphics g)
{
iOb.showType(g, 10,10);
strOb.showType(g, 30,10);
}
class Gen<T>
{
// объявление настраиваемого класса Gen
...
}
}
Итак, в апплет передается два параметра. Один из них – целое число,
другой – символьная строка. Для хранения этих данных в классе GenDemo
объявляются ссылки на объекты типов Gen<Integer> и Gen<String>. Типы
данных Integer и String в данном случае являются параметрами настраиваемых типов. При создании первой ссылки параметру T класса Gen присваивается значение Integer. Это значит, что генерируется реализация класса Gen
для работы с целыми числами и iOb является объектом именно этого класса.
Во втором случае аналогичная работа проводится со значением типа String и
объектом strOb. Далее при работе с объектом iOb везде, где упоминался тип
Т в классе Gen используется тип Integer, а при работе с объектом strOb тип
Т становится типом String.
123
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
При создании настраиваемых классов требуется иметь в виду следующее правило. При объявлении объекта настраиваемого типа данных аргумент
типа, передаваемый классу как параметр, должен быть каким-либо классом.
Использование в качестве аргумента типа стандартных типов данных,
например, int или char, не разрешается. Если же возникнет такая необходимость, требуется использовать класс-оболочку, соответствующий нужному
стандартному типу данных.
124
Практикум по программированию на языке Java
Приложение 4. Работа с JDK в режиме командной строки
Бывают ситуации, когда с пакетом JDK приходится работать непосредственно из командной строки, минуя какие-либо графические оболочки.
Для этого операционную систему необходимо предварительно настроить.
Эта настройка производится один раз. Рассмотрим правила настройки для
самой распространенной в настоящее время операционной системы –
Windows XP.
Операционная система должна иметь информацию, какой каталог JDK
содержит программы, нужные для компиляции и запуска апплетов и приложений. Поэтому следует настроить переменную среды Path. Для этого выполните следующие действия.
 Выберите "Пуск" – "Панель управления" – "Администрирование" –
"Управление компьютером" и в левой части окна щелкните правой
кнопкой мыши по строке "Управление компьютером".
 В появившемся контекстном меню выберите пункт "Свойства", затем в
окне свойств перейдите на закладку "Дополнительно".
 В группе "Переменные среды" нажмите на кнопку "Параметры".
 В списке "Системные переменные" выберите переменную Path и
нажмите на кнопку "Изменить".
 В текстовом поле "Значение переменной" добавьте точку с запятой и
полное имя каталога BIN пакета JDK. Например, если JDK установлен в
каталог C:\j2sdk1.4.0, то это имя будет выглядеть как C:\j2sdk1.4.0\bin.
Соответственно, если переменной Path в списке не было, тогда ее требуется создать, и в этом случае точку с запятой в значении переменной указывать не надо.
Теперь можно работать с командной строкой. Выберите "Пуск" –
"Выполнить", наберите и выполните команду cmd. Появится черное окно
командной строки – окно DOS, в котором указывается строка приглашения в
125
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
виде имени текущего каталога. В этом окне можно выполнять команды только в текстовом режиме. Для вывода списка стандартных команд наберите
help и нажмите Enter. Чаще всего приходится использовать команды перемещения по каталогам, например:
d: – переход к диску D;
cd .. – переход к родительскому каталогу;
cd MyJava – переход к подкаталогу MyJava текущего каталога (относительная ссылка);
cd c:\MyJava – переход к подкаталогу MyJava диска C (абсолютная
ссылка).
По списку ранее введенных команд можно перемещаться стрелками вверхвниз.
Теперь перейдем к работе с JDK. Предположим, мы хотим откомпилировать какой-то апплет. Сначала следует перейти в каталог, в котором находится Java-файл этого апплета. Для компиляции используется программа javac:
javac FirstApplet.java
Если в программе были ошибки, они будут выведены в окно, если же ошибок
не было, то в окне снова появится строка приглашения. Обратите внимание,
что расширение файла .java указывать обязательно.
Теперь мы хотим просмотреть результат работы апплета. Для этого
служит команда appletviewer.
appletviewer FirstApplet.html
Обратите внимание, что в качестве параметра в этой команде указывается
HTML-файл. После выполнения этой команды будет выведено отдельное ок126
Практикум по программированию на языке Java
но с запущенным апплетом (JCreator использует то же самое окно для запуска апплетов).
Для компиляции приложений используется та же команда javac, что и
для апплетов, например,
javac ChatServer.java
А запуск приложений выглядит несколько по-другому. Для этого служит команда java. Например:
java ChatServer
Регистр символов в названии класса является существенным! Запустите эту
команду на выполнение. Возможно, что вы получите сообщение:
Exception in thread "main"
java.lang.NoClassDefFoundError: ChatServer
Данное
сообщение
означает,
что
Java-машина
не
находит
файл
ChatServer.class, хотя он и находится в текущем каталоге. Для исправления
этой ошибки следует вернуться к настройке переменных среды и создать переменную Classpath. Эта переменная указывает, где Java-машина должна
искать нестандартные классы. В качестве значения переменной укажите символ "." – это соответствует текущему каталогу.
Таким же образом подключаются и другие нестандартные классы,
например, пакет для работы с MySQL. Пусть он называется mysqlconnector-java-3.0.8-stable-bin.jar и находится в каталоге C:\mysql. Для использования этого пакета в значение переменной Classpath через точку с запятой добавьте строку C:\mysql\mysql-connector-java-3.0.8-stable-bin.jar.
Обратите внимание, что при работе с окном командной строки русские
символы выводятся в кодировке DOS, поэтому, например, кодировка
Win1251 в этом окне будет нечитаемой.
127
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
Приложение 5. Коды примеров
Пример 1. Приложение, использующее потоки.
class WalkmanHire {
/* Прокат плееров-экскурсоводов
J M Bishop Январь 1997
* Программа имитирует прокат плееров для посетителей музея.
* Имеется некоторое фиксированное количество плееров и не* сколько служащих, которые их выдают посетителям под денежный
* залог и принимают обратно с возвратом залога.
*
*
*
*
*
По умолчанию имеется 50 плееров и 3 сотрудника. Эти значения
можно изменить, если запустить программу с параметрами, например,
java WalkmanHire 100 8.
Изначально сумма полученного залога равна 0.
* Иллюстрируют применение синхронизации, а также
* использование методов wait и notify.
* В программе одновременно используются потоки двух разных
* типов – класс "Группа посетитителей" и класс "Сотрудник"
*/
public static void main (String [] args) {
// получение числа плееров, начало имитации
// деятельности музея
if (args.length >= 1)
pool = Integer.parseInt(args[0]);
else pool = 50;
Museum m = new Museum (pool);
// получение количества сотрудников,
// создание и запуск потоков для имитации работы сотрудников
if (args.length >= 2)
helpers = Integer.parseInt(args[1]);
else helpers = 3;
for (int i=0; i<helpers; i++)
new Counter (m,i).start();
}
static int pool;
static int helpers;
}
import java.io.*;
class Museum {
Museum (int w) {
walkmen = w;
128
Практикум по программированию на языке Java
cash = 0;
}
synchronized void hire (int c,int n) {
// Если недостаточно свободных плееров,
// ждать, пока не будет произведен возврат количества,
// необходимого для текущей группы посетителей
System.out.println("Сотруднику "+c+" требуется "+n+
" плееров");
while (walkmen < n) {
try { wait(); }
catch (InterruptedException e) {}
}
// Выдать плееры и принять залог.
// Пробудить следующий поток, находящийся в состоянии
// ожидания.
walkmen -= n;
cash += n;
System.out.println("Сотрудник "+c+" выдал "+n+" плееров");
System.out.println("Текущее состояние:"+
" Залог: "+cash+" Всего: "+(walkmen+cash)
+ " Плееры в наличии: "+walkmen);
notify ();
}
synchronized void replace (int n) {
// Принять плееры, вернуть залог и пробудить следующий
// поток, находящийся в состоянии ожидания
System.out.println("Возвращено "+n+" плееров");
walkmen +=n;
cash -= n;
notify ();
}
private static int walkmen;
private static int cash;
}
class Visitors extends Thread {
Visitors (Museum m, int w) {
museum = m;
groupSize = w;
}
public void run () {
// Группа осматривает экспозицию музея в течение случайного
129
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
// интервала времени (от 1 до 2 секунд), затем возвращает
// плееры и покидает музей.
// Поток "Группа посетителей" уничтожается.
try {sleep((int) (Math.random()*1000)+1);}
catch(InterruptedException e) {}
museum.replace(groupSize);
}
Museum museum;
int groupSize;
}
class Counter extends Thread {
Counter (Museum m, int q) {
museum = m;
queue = q;
}
public void run () {
// Генерируется количество посетителей в новой группе,
// выдается нужное количество плееров,
// создается и запускается объект-поток
// для группы посетителей.
while (true) {
int w = a(7);
museum.hire(queue, w);
new Visitors (museum, w).start();
// Генерируется пауза перед приходом
// следующей группы посетителей
try {sleep(a(100));} catch(InterruptedException e) {}
}
}
Museum museum;
int queue;
static int a (int x) {
return (int) (Math.random() * x) +1;
}
}
Пример 2. Два потока, соединенные каналом ввода-вывода. Один поток рисует фейерверк, другой формирует для него цвет
/* Апплет, иллюстрирующий обмен данными с помощью каналов ввода* вывода. Апплет производит рисование залпов фейерверка.
* Рисованием фейерверка занимается объект-поток типа
* ZalpThr. Другой поток (типа ColorThr) случайным образом
* генерирует цвет очередной серии залпов.
130
Практикум по программированию на языке Java
* Для передачи сгенерированного цвета в объект-поток рисования
* фейерверка используются канальные потоки ввода-вывода,
* которые создаются в объекте-апплете.
*/
import java.awt.*;
import java.applet.*;
import java.io.*;
// апплет для рисования фейерверка
public class Zalp extends Applet
{
// канальный поток ввода (для чтения сгенерированного цвета)
PipedInputStream in=null;
// канальный поток вывода (для передачи сгенерированного цвета)
PipedOutputStream out=null;
public void init()
{
// создание канальных потоков ввода-вывода
try
{
in = new PipedInputStream();
// поток вывода создается на основе потока ввода
// для организации коммуникационного канала
// между ними
out = new PipedOutputStream(in);
}
catch(IOException ex)
{
}
setBackground(Color.black);
// создание потока рисования фейерверка,
// в качестве параметров конструктора передаются
// поток ввода для получения цвета рисования и
// графический контекст апплета
new ZalpThr(in,getGraphics()).start();
// создание потока генерации цвета фейерверка,
// в качестве параметров конструктора передается
// поток вывода для передачи цвета
new ColorThr(out).start();
}
}
import java.io.*;
// класс-поток для генерации цвета фейерверка
class ColorThr extends Thread
{
PipedOutputStream out;
131
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
ColorThr(PipedOutputStream out)
{
this.out=out;
}
public void run()
{
while (true)
{
try
{
// генерация компонентов цвета и отправка
// их через канал вывода потоку рисования
// фейерверка
out.write(100+(int)(Math.random()*155));
out.write(100+(int)(Math.random()*155));
out.write(100+(int)(Math.random()*155));
}
catch(IOException ex)
{}
try
{
// задается пауза до генерации
// следующего цвета
sleep((int)(Math.random()*10000));
}
catch(InterruptedException e)
{}
}
}
}
import java.awt.*;
import java.io.*;
// класс-поток для рисования фейерверка
class ZalpThr extends Thread
{
int M=300,N=300;
int zx,zy;
int zd;
boolean znew=true;
int r,g,b;
PipedInputStream in=null;
Graphics gr;
ZalpThr(PipedInputStream in,Graphics gr)
{
this.in=in;
this.gr=gr;
}
public void run()
132
Практикум по программированию на языке Java
{
while (true)
{
if (znew)
{
zx=(int)(Math.random()*M);
zy=(int)(Math.random()*N);
zd=0;
znew=false;
// проверяем наличие данных в потоке ввода
// если они там есть, то считываем их
try
{
if (in.available()>0)
r=in.read();
if (in.available()>0)
g=in.read();
if (in.available()>0)
b=in.read();
}
catch(IOException ex)
{
System.out.println("Exception!!!");
}
}
else
{
zd+=10;
if(zd>=M)
znew=true;
}
paint();
try
{
sleep(50);
}
catch(InterruptedException e)
{
}
}
}
public void paint()
{
int x,y;
int d;
Color col=new Color(r,g,b);
gr.setColor(col);
gr.clearRect(0,0,M,N);
if (zd==0)
{
133
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
gr.fillOval(zx,zy,10,10);
}
else if (zd<=M/2)
{
double dvaPI=2*Math.PI;
double step=dvaPI/100;
for (double i=0; i<dvaPI; i+=step)
{
d=zd/2+(int)(Math.random()*zd/2);
x=zx+(int)(d*Math.cos(i));
y=zy-(int)(d*Math.sin(i));
gr.fillOval(x,y,5,5);
}
}
}
}
Пример 3. Чат-сервер
/* Чат-сервер
J M Bishop Январь 1997
* Модифицированная версия - Java 1.1 Январь 1998
* Создает сервер для обмена сообщениями.
*
* Подключение клиента:
* telnet x y
* где x и y - имя и порт компьютера,
* на котором запущен сервер.
*
* Иллюстрирует сокеты, ввод-вывод на сокетах
* потоки, синхронизацию и использование списков.
*/
import java.io.*;
import java.net.*;
public class ChatServer {
private static List clientList = new List();
private static int id = 0;
public static void main(String[] args) throws IOException {
// Назначаем порт и создаем сокет.
int port = 8190;
if (args.length > 0)
port = Integer.parseInt(args[0]);
ServerSocket listener = new ServerSocket(port);
System.out.println("The Chat Server is running " +
" on port "+port);
// Ожидаем (слушаем) новых клиентов.
// Для каждого клиента создаем поток-обработчик.
134
Практикум по программированию на языке Java
// Добавляем клиента в связный список.
while (true) {
Socket client = listener.accept();
new ChatHandler(client).start();
System.out.println("New client no."+id+
" from "+ client.getInetAddress()+
" on client's port "+client.getPort());
clientList.reset();
clientList.add(client);
id++;
}
}
static synchronized void broadcast
(String message, String name)
throws IOException {
// Посылаем сообщение всем клиентам.
Socket s;
PrintWriter p;
for (clientList.reset();
!clientList.eol(); clientList.succ())
{
s = (Socket)clientList.current();
p = new PrintWriter(s.getOutputStream(), true);
p.println(name+": "+message);
}
}
static synchronized void remove(Socket s) {
// Ищем клиента в списке (по ссылке на его сокет)
// и удаляем его из списка.
Socket t;
for (clientList.reset();
!clientList.eol(); clientList.succ())
{
t = (Socket)clientList.current();
if (t.equals(s))
break;
}
clientList.remove();
id--;
}
}
class ChatHandler extends Thread {
// Класс ChatHandler вызывается из ChatServer:
// один поток для каждого клиента, подключившегося к чату.
private BufferedReader in;
private PrintWriter out;
135
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
private Socket toClient;
private String name;
ChatHandler(Socket s) {
toClient = s;
}
public void run() {
try {
// Создаем потоки ввода-вывода для сокета
// и печатаем приглашение.
in = new BufferedReader(new InputStreamReader(
toClient.getInputStream()));
out = new PrintWriter(toClient.getOutputStream(),
true);
out.println("*** Welcome to the Chatter ***");
out.println("Type BYE to end");
out.println("What is your name? ");
out.flush();
String name = in.readLine();
ChatServer.broadcast(name+
" has joined the discussion.", "Chatter");
// Читаем строки сообщения
// и отправляем сообщение всем клиентам.
while (true) {
String s = in.readLine().trim();
// Проверяем первые три символа для BYE.
// Избегаем проблем с разными символами конца строки.
if (s.length() > 2 && s.charAt(0) == 'B' &&
s.charAt(1) == 'Y' && s.charAt(2) == 'E') {
ChatServer.broadcast(name+
" has left the discussion.","Chatter");
break;
}
ChatServer.broadcast(s, name);
}
ChatServer.remove(toClient);
toClient.close();
} catch (Exception e) {
System.out.println("Chatter error: "+e);
}
}
}
class List
//
//
//
//
136
{
Класс Список, J M Bishop Октябрь 1997
Поддерживает список объектов
в порядке LIFO (Last-in-First-out),
содержит метод-итератор.
Практикум по программированию на языке Java
private Node start, now, prev;
public List() {
now = null;
start = null;
prev = null;
}
public void add(Object x) {
if (start == null) {
start = new Node(x, null);
now = start;
} else {
Node T = new Node(x, now.link);
now.link = T;
prev = now;
now = T;
}
}
public void remove() {
if (isempty() || eol()) {
return;
} else {
if (prev == null) {
start = now.link;
} else {
prev.link = now.link;
now = now.link;
}
}
}
public boolean isempty() {
return start == null;
}
public Object current() {
return now.data;
}
public void reset() {
now = start;
prev = null;
}
public boolean eol() {
return now == null;
}
public void succ() {
137
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
now = now.link;
if (prev == null)
prev = start;
else
prev = prev.link;
}
class Node {
// Класс Node для хранения объектов,
// которые связаны в список.
Node link;
Object data;
Node(Object d, Node n) {
link = n;
data = d;
}
}
}
Пример 4. HTML-форма, передающая параметры сервлету. Данная форма
предназначена для ввода и отсылки данных при оформлении заказа в электронном магазине.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD><TITLE>Оформление заказа</TITLE></HEAD>
<BODY BGCOLOR="#FDF5E6">
<H1 ALIGN="CENTER">Оформление заказа</H1>
<FORM ACTION="/servlet/ShowParameters" METHOD="POST">
Код товара: <INPUT TYPE="TEXT" NAME="itemNum"><BR>
Количество: <INPUT TYPE="TEXT" NAME="quantity"><BR>
Цена за штуку: <INPUT TYPE="TEXT" NAME="price" VALUE="$"><BR>
<HR>
Имя: <INPUT TYPE="TEXT" NAME="firstName"><BR>
Фамилия: <INPUT TYPE="TEXT" NAME="lastName"><BR>
Адрес доставки:
<TEXTAREA NAME="address" ROWS=3 COLS=40></TEXTAREA><BR>
Вид кредитной карты:<BR>
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Visa">Visa<BR>
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Master Card">Master Card<BR>
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Amex">American Express<BR>
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Discover">Discover<BR>
138
Практикум по программированию на языке Java
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Java SmartCard">Java SmartCard<BR>
Номер кредитной карты:
<INPUT TYPE="PASSWORD" NAME="cardNum"><BR>
Повторить номер кредитной карты:
<INPUT TYPE="PASSWORD" NAME="cardNum"><BR><BR>
<CENTER>
<INPUT TYPE="SUBMIT" VALUE="Оформить заказ">
</CENTER>
</FORM>
<br>
</BODY>
</HTML>
Сервлет, получающий параметры из HTML-формы. В результате работы
сервлета должен быть сформирован HTML-документ, содержащий таблицу с именами переданных параметров и их значениями.
import
import
import
import
java.io.*;
javax.servlet.*;
javax.servlet.http.*;
java.util.*;
public class ShowParameters extends HttpServlet
{
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Reading All Request Parameters";
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0"+
" Transitional//EN\">\n" +
"<HTML>\n" +
"<HEAD><TITLE>List of parameters"+
"</TITLE></HEAD>\n" +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"<TABLE BORDER=1 ALIGN=CENTER>\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
"<TH>Parameter Name<TH>Parameter Value(s)");
// создаем объект-перечисление и заполняем его
// именами параметров формы
Enumeration paramNames = request.getParameterNames();
// запускаем цикл перебора элементов перечисления
while(paramNames.hasMoreElements())
{
// получаем следующий элемент перечисления
String paramName = (String)paramNames.nextElement();
out.print("<TR><TD>" + paramName + "\n<TD>");
139
О.В. Пинягина, О.А. Кашина, А.А. Андрианова
// создаем массив строк и записываем в него
// переданные из формы значения параметра,
// имя которого хранится в переменной paramName
String[] paramValues =
request.getParameterValues(paramName);
if (paramValues.length == 1) {
// параметр имеет одно значение
String paramValue = paramValues[0];
// проверка на пустоту значения параметра
if (paramValue.length() == 0)
out.println("<I>No Value</I>");
else
out.println(paramValue);
} else {
// параметр имеет много значений (например,
// список со множественным выбором)
out.println("<UL>");
for(int i=0; i<paramValues.length; i++) {
out.println("<LI>" + paramValues[i]);
}
out.println("</UL>");
}
}
out.println("</TABLE>\n</BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
doGet(request, response);
}
}
140
Практикум по программированию на языке Java
Литература
Основная литература, использованная при подготовке данного курса:
1. Бишоп, Дж. Эффективная работа: Java 2 [Текст]: пер. с англ./ Джуди
Бишоп. – СПб.: Питер, 2002. – 592 с.
2. Friesen, G. Java 2 by example [Текст] / Geoff Friesen. – Que, 2000. – 848
p.
3. Джамса, К. Изучи сам. Java сегодня [Текст]: пер. с англ. / Крис Джамса. – Мн.: ООО "Попурри", 1996. – 416 с.
4. Холл, М. Сервлеты и Java Server Pages [Текст]: пер. с англ. / Марти
Холл. – СПб.: Питер, 2001. – 496 с.
Дополнительная литература:
1. Эккель, Б. Философия Java [Текст]: пер. с англ. / Брюс Эккель. – СПб.:
Питер, 2003. – 976 с.
2. Брогден, Б. Электронный магазин на Java и XML [Текст]: пер. с англ. /
Б. Брогден, К. Минник. – СПб.: Питер, 2002. – 400 с.
3. Мугал, Халид А. Java. Руководство по подготовка к сдаче сертификационного экзамена CX-310-035 [Текст]: пер. с англ. / Халид А. Мугал,
Рольф В. Расмуссен. – М.: КУДИЦ-ОБРАЗ, 2006. – 688 с.
4. Шилдт, Г. Java 2 v5.0 (Tiger). Новые возможности [Текст]: пер. с англ.
/ Герберт Шилдт. – СПб.: БХВ-Петербург, 2005. – 196 с.
141
Download