
- •Введение в Java
- •Что такое апплет?
- •Пишем первый апплет
- •Рисуем прямоугольники и эллипсы
- •Закрашиваем фон
- •Информация в строке состояния браузера
- •Класс Color
- •Изменяем шрифт
- •Мигающая надпись
- •Бегущая строка
- •Работаем с мышью
- •Интерфейс MouseListener
- •Интерфейс MouseMotionListener
- •Класс MouseEvent
- •Метод update()
- •Работаем с клавиатурой
- •Вставляем изображение в апплет
- •Заполение фона градиентом
- •Передача данных в апплет
- •Начинаем писать игру "Убей муху"
- •Продолжение игры
Введение в Java
Что такое Java и как она работает?
Если кратко, то Java - это один из языков программирования. Он разработан компанией Sun, и является платформо-независимым. Это означает, что программа, написанная на Java, будет одинаково выполнятся и под Windows, и под Linux, и под другими ОС. Достигается это следующим образом - текст программы переводится с помощью компилятора не в родные для процессора команды, а в коды виртуальной java-машины (JVM). Коды виртуальной машины одинаковы на любой платформе, и именно поэтому одна и та же программа и будет работать на разных платформах. Коды эти, кстати, называются байт-кодами. Сама же виртуальная машина от платформы, естественно, зависит - виртуальная машина для Windows отличается от виртуальной машины для других ОС. Мы будем рассматривать создание программ на Java для Windows. Но это не означает, что они не будут работать на других платформах - как раз наоборот.
Существует два основных вида программ на Java - собственно Java-программы и апплеты. Первые выполняются как самостоятельные программы, вторые выполняются в браузере. В настоящее время почти все браузеры имеют в своем распоряжении JVM. Слово почти означает, что Internet Explorer 6.0 не поддерживает JVM, но вы можете использовать продукты третьих фирм для исправления этого. У Microsoft вообще особая позиция по Java - сначала Microsoft поддерживала этот язык, но затем отказалась. Сейчас Microsoft активно продвигает свой новый язык C# и платформу .NET как альтернативу Java. В свое время Microsoft даже проиграла компании SUN судебное разбирательство по поводу нарушения лицензионного соглашения по Java - реализация Java у Microsoft была привязана к платформе Windows.
Что такое JDK и как его установить?
JDK расшифровывается как Java Developer Kit. Это набор программ и утилит, предназначенный для программирования на Java. В него ряд утилит. Вот некоторые из них:
Компилятор javac. Именно он и переводит текст программы на Java в байт-коды виртуальной машины.
Интерпретатор java. Вы с его помощью будете запускать откомпилированные в байт-коды программы. Он содержит в себе JVM (Виртуальную машину Java).
Утилита appletviewer. С ее помощью можно запускать созданные вами апплеты. Фактически она представляет из себя браузер, который может запускать только апплеты.
Утилита javadoc. Она предназначена для создания документации.
Есть еще и другие утилиты, но пока они нам не особенно нужны, так что обсуждать мы их не будем.
JDK - это бесплатный набор. Как следствие, вам придется работать без особого комфорта - тексты программ надо будет набирать в Блокноте или аналогичном текстовом редакторе. Скачать JDK можно с сайта компании Sun. Текущая версия - 1.3, хотя на момент написания этих строк и версия 1.4 уже не за горами. В терминах Sun нужная нам версия имеет номер 2 (т. е. с сайта Sun вы должны качать Java 2). Перед скачиванием убедитесь, что нашли JDK для нужной платформы (Windows в нашем случае).
Категории программ Java
● Приложение (application) – аналог “обычной” прикладной программы.
● Апплет (applet) – специализированная программа, работающая в окне WWW-документа под управлением браузера.
● Сервлет (servlet) - специализированная программа, работающая в WWW на стороне сервера.
● Модуль EJB (Enterprise JavaBeans) – предназначен для многократного использования серверными
приложениями Java.
● Библиотека – предназначена для многократного использования программами классов Java
SDK - Software Development Kit
Поставить на компьютер исполняющую среду Java (JVM) можно путём установки SDK – Комплекта разработки программного обеспечения. Имеется три типа SDK:
● Java ME – комплект Java Micro Edition, предназначенный для программирования “тонких клиентов”.
● Java SE – комплект Java Standard Edition, предназначенный для программирования обычных компьютеров.
● Java EE– комплект Java Enterprise Edition, предназначенный для написания серверного программного обеспечения.
Идентификаторы - это имена переменных, процедур, функций и т.д. В идентификаторах можно применять только буквы и цифры, причём первой всегда должна быть буква, а далее может идти произвольная комбинация букв и цифр. Длина идентификатора в Java любая.
Переменная – это именованная ячейка памяти, содержимое которой может изменяться. При объявлении переменной сначала указывается тип переменной, а затем идентификатор задаваемой переменной.
Первая программа на Java
Первая программа, по давно укоренившейся традиции, будет HelloWorld. Ниже приводится ее текст, который надо набрать в любом текстовом редакторе, позволяющем сохранять документ в ASCII-кодах (лучше всего для этих целей подходит Блокнот). Наберите следующий текст и сохраните его в файле HelloWorld.java:
class HelloWorld{
public static void main(String [] args){
System.out.println("Hello World!");
}
}
Несколько слов по тексту программы. Во-первых, обратите внимание на слово class. Любая программа на Java использует классы, и эта - не исключение. Во-вторых, обязательно должен быть метод (функция) main. Именно с него все и начинается. В нашем случае main имеет несколько модификаторов. Модификатор public означает, что данный метод будет виден снаружи класса. Модификатор static приблизительно означает, что метод main можно вызывать не для конкретного экземпляра класса, а в самом начале программы, когда никакого экземпляра класса еще нет. И, наконец void означает, что метод main не возвращает никакого значения. В строке
...
System.out.println("Hello World!");
...
вызывается метод println, который и выводит на экран соответствующую надпись. Этот метод берется из пространства имен System.out.
Еще одно замечание. Java, как и все C-подобные языки, различает строчные и прописные буквы. Так что, например, HelloWorld и helloworld - разные вещи.
После того, как текст набран (напомним, что мы сохраняем его в файле с именем HelloWorld.java), его надо откомпилировать. Для этого в командной строке (Для ее вызова в Windows 2000 и XP выберите Start->Run и затем наберите cmd) перейдите в папку с нашим файлом и наберите javac HelloWorld.java:
Буковка c в конце слова javac - это от английского слова компилятор. Если все было сделано правильно, то никаких сообщений выдаваться не должно, а в нашей папке должен появиться еще один файл HelloWorld.class. Именно он и представляет из себя откомпилированную в байт-коды нашу программу. Для его запуска набираем в командной строке java HelloWorld:
Обратите внимание, что имя файла мы набираем без расширения.
Результатом выполнения программы будет, как и ожидалось, вывод на экран слов "Hello World!":
С первой программой все!
Числовые типы данных
Числовые типы данных Java перечислены в следующей таблице:
Тип |
Описание |
Количество байтов |
int |
целый |
4 |
float |
вещественный |
4 |
char |
символьный |
2 |
short |
короткое целое |
2 |
long |
длинное целое |
8 |
double |
длинное вещественное |
8 |
byte |
байт |
1 |
Объявление переменных происходит следующим образом:
int a; //Переменная a целого типа
float f1, f2=55.88; //Две переменные вещественного типа
В Java переменным сразу при объявлении задаются стандартные значения (ноль для числовых переменных, false для логических). Так, в приведенном примере в f1 будет 0, а в f2 - 55.88.
Для переменных числового типа определены стандартные арифметические операции: +, -, *, /. Назначение их понятно - все, как в других языках. Как и в других C-подобных языках, есть остаток от деления и операции увеличения/уменьшения на один (%, ++, --). Вот пример:
static int k=21, w=10;
...
int z=k%w;
System.out.println(z); //z=1
k++;
System.out.println(k); //k=22
w--;
System.out.println(w); //w=9
Операторы ++ и -- можно писать как после, так и до переменной. Чаще всего это все равно, но иногда порядок важен. Вот пример:
static int k=10, w=10;
...
int z=k++;
System.out.println(z); //z=10, k=11
z=++n;
System.out.println(z); //z=11, n=11
Т. е. у двух форм оператора ++ (это относится и к --) разное возвращаемое значение - в одном случае первоначальное число, а в другом - измененное.
Логический тип
Переменные логического типа могут принимать значение или true (истина), или false (ложь). Сразу обратите внимание, что нельзя вместо true и false писать нулевое и ненулевое значения (как, например, это можно делать в C/C++). Такое замены в Java нет.
Для переменных логического типа существуют операции & (и), && (и), | (или), || (или), ! (не), ^ (исключающее или). Обратите внимание, что для "и" и "или" существует два варианта. Об их различии чуть ниже. Пока же приведем таблицы истинности:
Оператор |
Описание |
Результат |
&& или & |
и |
Результат true только тогда, когда оба операнда равны true |
|| или | |
или |
Результат false только тогда, когда оба операнда равны false |
^ |
исключающее или |
Результат true только тогда, когда ровно один из операндов равен true |
! |
не |
Изменяет значение на противоположное (true на false, false на true) |
== |
логическое равно |
Применяется к переменым любого типа. Результат true, если оба операнда равны true, false - в противном случае. |
Теперь о различии между && и & (|| и |). Операторы & и | - это обычные логические операторы, && и || - сокращенные. Вот конкретный пример для && и &:
...
k=20;
if (k<0 & k/0>1) //деление на 0 и возникнет ошибка
{
System.out.println(k);
}
if (k<0 && k/0>1) //деление на 0, но ошибки нет
{
System.out.println(k);
}
...
Т. е. в первом случае (с одним &) проверяются все части логического выражения, а во втором (с двумя &&) правая часть не проверяется (так как левая равна false).
Вот пример и для "или":
...
k=20;
if (k>0 | k/0>1) //деление на 0 и возникнет ошибка
{
System.out.println(k);
}
if (k>0 || k/0>1) //деление на 0, но ошибки нет
{
System.out.println(k);
}
...
Здесь во втором случае так как левая часть равна true, то правая часть вообще проверяться не будет (и ошибка, соответственно не возникнет).
Также обратите внимание, что в качестве логического равно (т. е. когда мы отвечаем на вопрос, верно ли что что-то равно чему-то) используется двойное равно (==). Одинарное же равно используется для присваивания:
if(a==b){ //Если a равно b
{
a=c; //В a записываем c
}
Операторы ветвления
Операторов ветвления в Java два - if и switch. Первый позволяет пойти программе по одному из двух направлений, второй позволяет сделать выбор между большим числом вариантов (два, три, четыре, ...).
Вот пример программы, которая выводит результат деления одного числа на другое. Если знаменатель равен нулю, то деления не происходит.
int a=30, b=5;
System.out.println("a="+a);
System.out.println("b="+b);
if (b!=0)
{
System.out.println(a/b);
}
else
{
System.out.println("На ноль делить нельзя!!!!");
}
Веточка else не обязательна. Если после проверки условия должен выполниться только один оператор, то фигурные скобки писать не обязательно.
Теперь пример с оператором switch. Пример смотрит, что за символ хранится в переменной ch (+, -, * или /), и в зависимости от этого делает то или иное действие с двумя числами. Результат действия выводится на экран.
char ch='/';
int k=40, n=10;
switch(ch)
{
case '+':
System.out.println(k+n);
break;
case '-':
System.out.println(k-n);
break;
case '*':
System.out.println(k*n);
break;
case '/':
System.out.println(k/n);
break;
default:
System.out.println("Error!");
}
Обратите внимание на break. Без него выполнялись бы операторы и в следующем case (пока не встретится break). Например, если написать так:
...
case 1:
case 2:
//Некоторые операторы
...
то "Некоторые операторы" будут выполнятся и когда проверяемая переменная в switch'е равна 1, и когда она равна 2.
Ветка default будет выполняться тогда, когда переменная в switch'е не равна ни одному значению в case'ах. Ее использование не обязательно. Если по задаче она не нужно, то не пишите ее.
Операторы циклов
Циклов в Java три вида - while, do-while и for. Первые два следует использовать тогда, когда точно неизвестно, сколько раз цикл должен выполнится. Цикл for используем тогда, когда число, которое наш код должен повторяться, известно.
Вот пример на цикл while:
int n=46;
int k=0;
while (k*k<=n)
{
k++;
}
В этом примере ищется такое минимальное k, что его квадрат больше n.
Цикл while и его брат цикл do-while выполняются до тех пор, пока условие в круглых скобочках истинно. Как только оно становится равным false, выполнение цикла прекращается.
Пример цикла do-while:
double k;
do
{
k=Math.random();
}while(k>0.1);
System.out.println(k);
В этом примере ищется первое случайное число меньшее 0.1. Как только оно сгенерировано датчиком случайных чисел Math.random(), выполнение цикла прекращается. Math.random() выдает случайные числа от 0 до 1.
Основное отличие циклов while и do-while в том, что while может вообще ни разу не выполнится (если условие в круглых скобочках сразу false), а do-while выполнится по крайней мере 1 раз.
Заметьте, что для циклов while и do-while где-то внутри цикла обязательно должна меняться переменная, стоящая в круглых скобочках после while. Иначе цикл может стать бесконечным.
И, наконец, пример цикла for:
for (int i=0; i<10; i++)
{
System.out.println(i*i);
}
Этот цикл распечатает квадраты целых чисел от 0 до 9. Обратите внимание, что переменная i объявлена прямо внутри цикла. Так часто и делается, так как чаще всего переменная-счетчик цикла вне его не нужна и не используется.
Массивы
Начнем сразу с примеров. Вот пример, в котором мы заводим массив из 3-х целых чисел, в каждое из которых мы записываем случайное целое число от 0 до 9 и затем выводим все числа на экран:
class Test{
public static void main(String [] args)
{
int [] k;
k=new int [3];
for(int i=0; i<3; i++)
{
k[i]=(int)(10*Math.random());
System.out.println(k[i]);
}
}
}
В этом примере мы завели массив k за два этапа - сначала мы объявили переменную типа массив целых чисел:
...
int [] k;
и затем мы определили массив (т. е. указали, сколько конкретно элементов в нем содержится):
...
k=new int [3];
Объявление массива можно сделать и другим способом:
...
int k[];
Т. е. квадратные скобочки можно ставить и так, и так. В общем-то особой разницы нет, и скобки можно ставить там, где удобнее в конкретной ситуации. Вот небольшой пример на два эти способа:
...
int n, k[]; //n - целое, k - массив из целых
int[] n, k; //n и k - массивы из целых
Два этапа для массива можно объединить:
...
int [] k = new int [3];
Нумерация элементов массива, как и в других C/C++-подобных языках, начинается с нуля.
Задание начальных элементов для массива можно совместить с его объявлением:
...
double[] d = {3.14, -44.43, 9.084};
В этом примере мы завели массив их трех вещественных чисел. Обратите внимание, что в этом случае можно в квадратных скобках ничего не писать.
Теперь рассмотрим двумерные массивы. Опять же начнем с примера:
class Test{
public static void main(String [] args)
{
int [][] k = {{3, 4, -44}, {-2, 8}};
System.out.println(k[0][2]);
System.out.println(k[1][1]);
}
}
В этом фрагменте мы заводим двумерный массив (так как у на две пару квадратных скобок) и тут же его инициализируем. Наш массив фактически представляет из себя массив массивов, т. е. в нашем массиве в качестве элементов содержаться два других одномерных массива - в одном из них три элемента, и в другом - два. Обратите внимание, что в Java массивы не обязательно "прямоугольные".
Точно также, как и для одномерных массивов, для многомерных существуют и другие способы их задания:
int [][] k;
k=new int[2][];
k[0]=new int[]{3, 4, -44};
k[1]=new int[]{-2, 8};
Также эквивалентны следующие объявления массивов:
...
double[][] d;
...
double[] d[];
...
double d[][];
Массивы могут участвовать в операциях присваивания:
class Test{
public static void main(String [] args)
{
int [][] k = {{3, 4, -44}, {-2, 8}};
int[][] k1;
k1=k; //k1 теперь ссылается на точ же массив, что и k
System.out.println(k1[0][2]);
System.out.println(k1[1][1]);
}
}
Указанный фрагмент выведет на экран -44 и 8.
Массивы в Java задаются динамически. Это в частности означает, что мы можем менять их размеры в процессе работы программы:
int [] k={3, 4};
System.out.println(k[1]);
k=new int[]{2, 6, -55};
System.out.println(k[2]);
В указанном фраменте в массиве k сначала 2 элемента, а затем - 3.
Начинаем изучать классы
С классом мы уже столкнулись ранее. И это не случайно - без классов на Java нельзя обойтись даже в самой простой программе. На этом же занятии и на нескольких следующих мы с вами и будем изучать классы и все, что с ними связано - наследование, конструкторы, виртуальные функции и другие мудреные вещи.
Давайте создадим новую программу. Вот ее текст:
class worker
{
private int Age;
public String Name;
public void setAge(int newAge)
{
if(newAge>=0)
Age=newAge;
else
Age=0;
}
public int getAge()
{
return Age;
}
}
class Test{
public static void main(String [] args){
worker wrk=new worker();
wrk.setAge(23);
wrk.Name="Ivan";
System.out.println(wrk.getAge() + "\n" + wrk.Name);
}
}
Сохраните эту программу в файле Test.java и откомпилируйте. При запуске наша программа должны выдать две строчки: 23 и Ivan.
Что мы в нашей программе делаем? Сначала мы определяем класс worker. Делается это с помощью ключевого слова class:
class worker
{
...
}
В классе мы определяем две переменные - Age для возраста и Name для имени. Кроме типа переменных (int и String) мы используем еще модификаторы доступа - private (означает, что наша переменная не будет видна снаружи класса) и public (с наружи класса доступ есть). Раз переменную Age мы объявили как private, то пишем два метода в нашем классе: setAge для чтения возраста и getAge - для записи. Эти методы мы объявляем с модификатором public, это значит, что мы сможем их вызывать снаружи класса. Метод getAge просто возвращает наш возраст, а метод setAge делает небольшую проверку, и записывает в Age только положительный возраст или нуль в противном случае. Если вы раньше программировали на C++, то обратите внимание, во-первых, что модификаторы доступа ставятся перед каждой переменной и перед каждым методом и во-вторых, что после закрывающей фигурной скобки класса точку с запятой ставить не надо.
Класс Test служит для испытания класса worker. В нем мы заводим экземпляр нашего класса:
...
worker wrk=new worker();
Это мы делаем за два этапа - сначала заводим переменную типа worker (которая является ссылкой на объект), и затем определяем сам объект (с помощью оператора new).
После создания объекта мы можем вызывать его методы, обращаться к открытым переменным и т. п. Это мы и делаем в строчках
...
wrk.setAge(23);
wrk.Name="Ivan";
System.out.println(wrk.getAge() + "\n" + wrk.Name);
...
Наследование классов
Классы можно писать не с нуля, а взяв за основу существующий класс. В этом случае эти классы будут является потомком и предком друг для друга. Потомка еще называют подклассом (subclass), а предка - суперклассом (superclass). Еще одна пара названий для таких классов - это класс-потомок и класс-предок.
Если один класс есть потомок другого, то он автоматически умеет делать все то, что умеет делать класс-предок. И нам остается только добавить в него то, чего не было в предке или изменить те методы, работа которых в классе-предке нас не удовлетворяет. В этом одна из главных черт Объектно-ориентированного программирования - если у некоторых объектов есть много общего, то можно для них создать класс-предок, в который записать все общие черты. Отличительные же черты будут реализованы в классах-потомках.
Давайте создадим класс boss, который будет потомком для класса worker из программы выше. Вот текст, который введите с том же файле Test.java, в котором находится класс worker:
class worker
{
...
}
class boss extends worker
{
public int NumOfWorkers; //Количество подчиненных
}
Ключевое слово extends означает, что наш новый класс boss есть потомок класса worker. Мы добавили в него только переменную NumOfWorkers, в которой будет храниться количество подчиненных. Класс же Test измените следующим образом:
...
public static void main(String [] args){
boss bigBoss=new boss();
bigBoss.setAge(41);
bigBoss.Name="Ivan Ivanov";
bigBoss.NumOfWorkers=100;
System.out.println(bigBoss.NumOfWorkers + "\n" + bigBoss.Name);
...
Как вы видите, кроме количества подчиненных для нашего bigBoss мы можем задавать имя и возраст. Они берутся из родительского класса. Все работает, как и ожидалось.
Конструкторы классов
Конструкторы предназначены для задания некоторых стандартных значений для переменных-членов класса. Конструктор - это тот же метод класса, только обладающий некоторым количеством особенностей. Раз это метод, значит мы должны после его имени писать круглые скобки, в которых мы можем писать или не писать параметры и т. п. Но есть и несколько черт, отличающих конструктор от других методов класса. Вот они:
Конструктор всегда называется так же, как и класс (т. е. если класс, скажем, называется worker, то и контруктор будет называться worker).
Конструктор в отличие от других методов вызывается сам в момент создания экземпляра класса.
Конструктор не возвращает никакого значения. Это значит, что если перед любым другим методом мы пишем тип возвращаемого значения (int, float и т. п.), то перед конструктором ничего писать не надо (void тоже писать не надо).
Приведем пример конструкторов для класса worker:
class worker
{
private int Age;
public String Name;
//Конструктор без параметров
public worker()
{
Age=20;
}
//Конструктор с параметрами
public worker(int newAge, String newName)
{
Age=newAge;
Name=newName;
}
...
}
Посмотреть на действия конструкторов в тестовом классе можно так:
...
worker wrk1=new worker();
worker wrk2=new worker(40, "Petrov");
System.out.println(wrk1.getAge() + "\n" + wrk1.Name);
System.out.println(wrk2.getAge() + "\n" + wrk2.Name);
Для первого работника вызовется конструктор без параметров и его возраст установится в 20, для второго - конструктор с параметрами, и его возраст станет равным 40, а имя - Petrov
Если вы не заведете в классе конструктора, то компилятор java создаст его сам.
worker wrk=new worker();
Здесь worker() - это как раз вызов конструктора без параметров. Создаваемый по умолчанию конструктор ничего не делает.
Абстрактные методы
Когда мы строим иерархию классов, то может оказаться, что некоторый метод должен присутствовать во всех классах, но во всех классах он имеет разную реализацию. Например, у нас может быть много типов работников (директора, программисты, бухгалтера, ...), и все классы для них будет потомками базового класса worker (так как у каждого из них есть возраст, имя, телефон и т. п. и дублировать все это во всех классах смысла нет). Но некоторые методы должны работать по-разному. Например, это может быть метод setSalary() для начисления зарплаты. Для работников с почасовой системой оплаты способ будет один, для совместителей - другой, для постоянных работников - третий.
Так вот, если метод должен присутствовать во всех классах-потомках, и в каждом из них он имеет свою реализацию, то такой метод объявляют в родительском классе абстрактным. Это означает, что в родительском классе мы не пишем реализацию этого метода (и не ставим после него фигурных скобок вообще). Вот пример:
abstract class worker
{
protected int Age;
public String Name;
abstract public void setSalary(int newSalary);
...
}
class boss extends worker
{
//Пишем реализацию абстрактного метода родительского класса
public void setSalary(int newSalary)
{
if(newSalary>=0)
Salary=newSalary;
else
Salary=3000;
}
...
}
class еngineer extends worker
{
//Пишем реализацию абстрактного метода родительского класса
public void setSalary(int newSalary)
{
if(newAge>=0)
Salary=newSalary;
else
Salary=2500;
}
...
}
Тут мы объявили класс worker, написали в нем абстрактный метод setSalary, и объявили два класса-потомка класса worker - классы boss и еngineer. В каждым из их мы пишем свою реализацию для метода setSalary.
Обратите внимание, что если мы объявили некий метод класса абстрактным, то и весь класс надо объявить абстрактным:
abstract class worker
...
Экземпляры абстрактного классы мы создавать не сможем - только экземпляры его потомков.
Для чего вообще возиться с абстрактными методами? Не проще ли его в классе предке вообще не объявлять? Дело в том, что вы можете в переменную типа родительского класса записать экземпляр класса потомка:
worker bigBoss=new boss();
Это значит, что вы можете в вашем программе объявить массив worker'ов, и записать в него всех работников - и инженеров, и директоров и простых работников. И тогда, если вам надо начислить зарплату для всех, то вы просто перебираете элементы этого массива.
Модификатор final
Иногда мы не хотим, чтобы от некоторого созданного нами класса можно было производить классы-потомки. Например, это может понадобиться в целях безопасности.
В этом случае мы объявляем класс с ключевым словом final. Вот пример:
final class someclass
{
//некоторые поля и методы класса
private int somedata;
...
}
Теперь от нашего класса someclass нельзя делать классы-потомки. Т. е тут, например, будет ошибка:
class newclass extends someclass //Ошибка!
{
...
}
Статические члены класса
Переменные, которые вы объявляете внутри класса, относятся к определенному экземпляру класса. Например, у класса worker, который мы рассматривали ранее, есть переменная Age (возраст). Понятно, что у одного работника возраст один, а у другого - другой, и эти два возраста между собой никак не связаны. С другой стороны, иногда нам нужна общая переменная на все экземпляры класса. Например, это может быть счетчик количества экземпляров класса. В этом случае такую переменную надо объявить с модификатором static. Вот пример:
class someclass
{
//Счетчик
static public int number;
//Конструктор
public someclass()
{
number++; //Увеличиваем счетчик
}
}
class Test{
public static void main(String [] args){
//Создаем два экземпляра класса someclass
someclass z1=new someclass();
someclass z2=new someclass();
System.out.println(someclass.number + "\n");
}
}
Как вы видите, переменная number объявлена как static. Это означает, что она одна на все экземпляры. В конструкторе она увеличивается на 1. Т. е. для первого экземпляра класса она будет равна 1, для второго - 2 и т. д. Что мы и проверяем в классе Test - заводим два экземпляра класса someclass, а затем выводим значение number. Естественно, что number будет равно 2. Обратите внимание, что в строке
...
System.out.println(someclass.number + "\n");
переменную number мы извлекаем не из конкретного экземплярра класса (z1, z2), а из самого класса someclass. Это возможно именно потому, что переменная number - статическая. Но, в принципе, в этом месте мы могли бы использовать и конкретный экземпляр класса - результат был бы тот же самый.
Первая оконная программа
Все программы, которые мы создавали до сих пор, были консольными. На этом занятии мы с вами создадим первую программу, которая будет будет иметь оконный вид. Ее можно будет запустить под Windows, и она будет выглядеть как обычная Windows-программа. Конечно, ее можно будет запускать не только под Windows, но и под любой ОС с установленной виртуальной машиной Java. Выглядеть она будет примерно одинаково во всех этих случаях.
Вот текст нашей первой оконной программы. Наберите его в любом текстовом редакторе.
import java.awt.*;
class First extends Frame{
public static void main(String[] args){
Frame fr=new First();
fr.setSize(400, 150);
fr.setVisible(true);
}
}
Сохраните текст в файле с именем First.java.
Небольшой комментарий для написанного нами кода. Для запуска нашей программы в отдельном окне мы создаем (естественно ;)) отдельный класс. У нас он назван First (также, кстати, называется и файл, в котором мы пишем этот текст). Для того, чтобы наше приложение могло работать в отдельном окне, мы объявляем наш класс потомком класса Frame. Для того, чтобы можно было использовать класс Frame, мы в начале программы пишем
import java.awt.*;
...
В классе мы заводим статический метод main, в котором мы создаем новый экземпляр класса Frame с помощью конструктора First(). Это, собственно, и будет окно нашей программы. Как вы видите, переменная fr имеет тип родительского класса (Frame), а записываем мы в нее экземпляр дочернего класса (First). Далее мы вызываем методы setSize (для установки начальных размеров) и setVisible (для показа окна на экране).
Запустите программу. Если вы работаете из командной строки, то наберите в ней java First и нажмите Enter. Если в некотором java-редакторе, то нажмите соответствующую комбинацию клавиш или кнопку.
Не пытайтесь закрыть это окно. Все равно это у вас не получится - ни Alt+F4 или крестик в правом верхнем углу не работают.
Читаем строку с клавиатуры
Вы, наверное, обратили внимание, что раньше мы старательно избегали получать данные от пользователя. Значения всем переменным мы задавали непосредственно в программе. Сейчас мы и узнаем, как же прочитать данные, введенные пользователем. Не все пока будет ясно (например, исключения), но код будет работать, и его можно применять в программах.
Вот пример класса, который умеет читать строку с клавиатуры и выводить ее на экран:
import java.io.*;
class Privet{public static void main(String [] args) throws
IOException{
System.out.println("Name, please ");
String s;
BufferedReader in=new BufferedReader(new
InputStreamReader(System.in));
s=in.readLine(); //Читаем с клавиатуры
System.out.println("Hi, "+s+"!");
}
}
Что мы тут делаем? Сначала мы подключаем java.io.*. Это сделано для того, чтобы не писать длинные имена. Можно было обойтись и без этого - вот так:
...
public static void main(String [] args) throws java.io.IOException{
...
java.io.BufferedReader in=new java.io.BufferedReader(
new java.io.InputStreamReader(System.in));
...
Но так слишком длинно, поэтому первую строчку и добавили.
Далее мы должны завести переменную для буферизованного ввода. Т. е. для вывода мы специальной переменной не заводили, а для ввода должны завести:
...
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
...
Переменная наша называется in.
Ну а потом совсем просто - методом readLine мы читаем с клавиатуры, и затем выводим на консоль (экран).
Читаем числа с клавиатуры
Вот пример класса, который может читать числа с клавиатуры:
import java.io.*;
class NumReader{
public static void main(String [] args) throws IOException{
String s;
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
s=in.readLine(); //Читаем с клавиатуры
System.out.println("s= "+s);
double d=Double.valueOf(s).doubleValue(); //Превращение строки в double
d++;
System.out.println("d= "+d);
}
}
Для показа, что мы прочитали именно число, мы его сначала увеличиваем на 1, и только после этого выводим на экран.
Если же нам надо превратить строку в целое, то мы должны использовать такую конструкцию:
...
int d=Integer.valueOf(s).intValue();
...
Если внимательно присмотреться, то можно увидеть, что мы тут используем странности - Integer вместо int и Double вместо double. Но именно так и должно быть. Дело в том, что это - классы. Класс целых чисел и класс вещественных.
Закрытие оконной программы
Давайте дополним код нашей первой оконной программы по крайней мере таким образом, чтобы она закрывалась:
import java.awt.*;
import java.awt.event.*;
class First extends Frame{
public static void main(String[] args){
Frame fr=new First();
fr.setSize(400, 150);
fr.setVisible(true);
fr.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent ev)
{
System.exit(0);
}
});
}
}
Немного пояснений по коду. Так как наша программа должна уметь реагировать на внешние события, то мы добавляем строку
...
import java.awt.event.*;
...
Далее мы вносим добавления в наш класс First. А именно для созданного внутри него экземпляра fr мы вызываем метод addWindowListener, который добавляет к ннашему классу возможность "прослушивать" оконные события. В качестве параметра метода addWindowListener мы создаем безымянный экземпляр класса WindowAdapter, внутри которого мы пишем обработчик для закрытия окна:
...
public void windowClosing(WindowEvent ev)
{
System.exit(0);
}
...
Понятно, что этот обработчик делает не что иное, как закрывает наше окно (конкретно это делает строка System.exit(0);).
Все! Компилируем и запускаем программу. Теперь наше окно стало еще больше походить на настоящее - его можно закрыть.
Библиотека классов Java - обзор
Классы Java объединяются в пакеты. В число основных пакетов Java входят следующие:
java.awt - классы и интерфейсы для создания пользовательского интерфейса
java.applet - классы для создания java-апплетов
java.io - классы для ввода-вывода (в том числе с консолью и файлами)
java.net - классы и интерфейсы для работы с сетью
java.math - классы для различных математических операций ()
java.lang - содержит наиболее общие для java классы и интерфейсы
java.util - пакет различных полезных классов (случайные числа, списки, даты и др.)
java.beans - пакет для создания компонентов JavaBeans
java.sql - пакет классов и интерфейсов для работы с базами данных
java.secuirity - пакет классов и интерфейсов для обеспечения безопасности
Пакет java.awt - обзор
Пакет java.awt предназначен для создания пользовательского интерфейса. Он, в частности, содержит классы для различных компонентов - кнопок, текстовых полей, классы для меню, классы для событий и др.
Вот основные классы этого пакета:
Button - кнопка
TextField - текстовое поле
Checkbox - флажок
CheckboxGroup - набор радиокнопок
Label - метка
List - список
TextArea - поле для ввода многострочного текста
Choice - выпадающий список
Image - абстрактный базовый класс для изображений
Frame - отображение окна операционной системы
Component - базовый класс для компонентов
Container - базовый класс для контейнеров
Panel - простой контейнер
BorderLayout - контейнер для компонентов
Dialog - базовый класс для диалоговых окон
FileDialog - класс для стандартного диалога открытия или сохранения файла
Events - класс для работы с событиями
AWTEvent - класс для событий AWT
Font - класс для работы со шрифтами
Color - класс для работы с цветами (в том числе в формате RGB)
MenuComponent - абстрактный базовый класс для всех классов меню
Menu - меню
MenuBar - меню на линейке меню
MenuItem - элемент меню
PopupMenu - контекстное меню
Cursor - курсор мыши
Библиотека Swing - делаем окно
В Java существует две библиотеки для создания пользовательского интерфейса - Awt и Swing. Swing считается более продвинутой и современной. Сегодня мы создадим окно с использованием этой библиотеки.
Итак, приступаем. Вот исходный текст нашей программы:
package progs;
// Импортируем нужные пространства имен.
import javax.swing.*;
// Класс основного окна программы.
public class MyFrame
extends JFrame {
// Конструктор.
public MyFrame(){
// Устанавливаем размеры и расположение.
setLocation(400, 200);
setSize(200, 200);
// Задаем заголовок окна.
setTitle("Title");
}
public static void main(String[] args) {
// Создание главного окна.
new MyFrame().setVisible(true);
}
}
Текст достаточно стандартен (создаем класс, производный от JFrame, после чего создаем его экземпляр в вызове статического метода main) и ясен из комментариев.
А вот так будет выглядеть наша программа после компиляции:
Отметьте, что окно будет закрываться при нажатии на крестик в правом верхнем углу - никакого кода нам для этого не пришлось писать.
Swing: добавляем кнопку с обработчиком
Посмотрим, как можно добавить к созданному ранее окну кнопку с обработчиком.
Наша кнопка будет экземпляром класса JButton. Вообще схема работы с элементами управления такая - сначала мы создаем контейнер для различных элементов управления, а потом созданные элементы добавляем к этому контейнеру.
Вот полный текст нашей программы:
package progs;
// Импортируем нужные пространства имен.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// Класс основного окна программы.
public class MyFrame
extends JFrame {
// Переменная для кнопки.
public JButton button;
// Конструктор.
public MyFrame(){
// Устанавливаем размеры и расположение.
setLocation(400, 200);
setSize(200, 200);
setTitle("Title");
// Задаем контейнер для компонентов.
Container con = getContentPane();
con.setLayout(new FlowLayout());
// Создание кнопки.
button = new JButton("Press me");
// Добавление кнопки к контейнеру.
con.add(button);
// Добавление обработчика для кнопки.
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
// Изменение заголовка окна.
setTitle("New Title");
}
});
}
public static void main(String[] args) {
// Создание главного окна.
new MyFrame().setVisible(true);
}
}
Логика нашей программы такая - создаем в главном окне контейнер, создаем кнопку, добавляем ее к контейнеру, добавляем обработчик для кнопки.
После запуска и компиляции и нажатия на кнопку наша программа будет выглядеть так:
Обратите внимание, что заголовок окна изменился - наш обработчик для кнопки действует!
Swing: кнопка с двумя состояниями
В библиотеке swing наряду с обычной кнопкой есть кнопка с двумя состояниями - нажатом и отжатом. Она работает приблизительно как checkbox, только выглядеть по-другому. Для создания такой кнопки в приложении мы используем класс JToggleButton.
Вот пример использования такой кнопки:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyFrame
extends JFrame {
// Переменная для кнопки.
public JToggleButton button;
// Конструктор.
public MyFrame(){
// Устанавливаем размеры и расположение.
setLocation(400, 200);
setSize(200, 200);
setTitle("Title");
// Задаем контейнер для компонентов.
Container con = getContentPane();
con.setLayout(new FlowLayout());
// Создание кнопки (срузу нажатой).
button = new JToggleButton("Press me", true);
// Добавление кнопки к контейнеру.
con.add(button);
// Добавление обработчика для кнопки.
button.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e){
// Изменение заголовка окна.
if(button.isSelected())
{
setTitle("Button is selected");
}
else
{
setTitle("Button isn't selected");
}
}
});
}
public static void main(String[] args) {
// Создание главного окна.
new MyFrame().setVisible(true);
}
public void windowClosing(WindowEvent ev) {
System.exit(0);
}
}
Часть нашей программы, в которой мы создаем окно, такая же, как и выше. Отличие только в том, что мы используем класс JToggleButton. С ним мы работаем как всегда - создаем экземпляр, вызываем конструктор (обратите внимание, что в конструкторе мы поставили второй параметр в true, что означает, что кнопка уже нажата), добавляем кнопку к контейнеру и добавляем обработчик для изменения состояния кнопки, в котором мы просто меняем заголовок нашей программы. Обратите внимание, что мы при добавлении этого обработчика указываем itemStateChanged - т. е. нас интересует не столько нажатие на кнопку, сколько изменение ее состояния.
После запуска нашей программы полуаем ожидаемый результат - кнопку с двумя состояниями (при этом в нажатом состоянии кнопка более темная):
Правильное закрытие программы
Созданные нами ранее программы работали не вполне корректно - а именно после своего закрытия они оставались в памяти. Убедится в этом было достаточно просто - запустив и закрыв программу несколько раз, можно было запустить Task Manager (Сtrl + Shift + Escape) и увидеть там несколько запущенных процессов javaw.exe:
Как правильно закрывать программу? Вот как - нам надо явно указать то действие, которое должно выполняться, когда пользователь закрывает программу. Это мы делаем путем вызова метода setDefaultCloseOperation с соответствующим параметром. Например, для правильного закрытия программы и выгрузкой ее из памати надо вызвать этот метод с параметром EXIT_ON_CLOSE (например, в конструкторе класса окна):
public class MyFrame
extends JFrame {
...
public MyFrame(){
// Задаем действие,
// выполняемое при выходе из программы.
setDefaultCloseOperation(EXIT_ON_CLOSE);
...
Теперь при закрытии программы все будет OK - в чем не трудно убедиться, запустив Task Manager.
Другие возможные значения параметра для этого метода это DO_NOTHING_ON_CLOSE (ничего не делать), HIDE_ON_CLOSE (просто спрятать программу, не выгружая ее из памяти) и др.
Swing: элемент JCheckBox
Элемент JCheckBox - это обычный checkbox (флажок). Он имеет вид квадратика, в котором может стоять (или не стоять) галочка. Этот элемент может иметь 22 состояния - либо включенное, либо выключенное. При этом состояние каждого флажка не зависит от других флажков.
Посмотрим, как практически сделать такой элемент.
Вот полный код:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyFrame
extends JFrame {
// Переменная для checkbox'а.
public JCheckBox checkBox;
// Конструктор.
public MyFrame(){
setDefaultCloseOperation(EXIT_ON_CLOSE);
// Устанавливаем размеры и расположение.
setLocation(400, 200);
setSize(200, 200);
setTitle("Title");
// Задаем контейнер для компонентов.
Container con = getContentPane();
con.setLayout(new FlowLayout());
// Создание checkbox'а.
checkBox = new JCheckBox("Java");
// Добавление checkbox'а к контейнеру.
con.add(checkBox);
// Добавление обработчика для checkbox'а.
checkBox.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e){
// Изменение заголовка окна.
if(checkBox.isSelected())
{
setTitle("CheckBox is selected");
}
else
{
setTitle("CheckBox isn't selected");
}
}
});
}
public static void main(String[] args) {
// Создание главного окна.
new MyFrame().setVisible(true);
}
public void windowClosing(WindowEvent ev) {
System.exit(0);
}
}
Самые существенные части этого кода следующие:
Во-первых, мы объявляем переменную для нашего checkbox'а:
public JCheckBox checkBox;
Во-вторых, мы этот checkbox создаем и присоединяем к нашему контейнеру:
// Создание checkbox'а.
checkBox = new JCheckBox("Java");
// Добавление checkbox'а к контейнеру.
con.add(checkBox);
В конструкторе мы сразу указываем ту надпись, которая будет рядом с checkbox'ом.
И в-третьих, мы добавляем обработчик для нашего checkbox'а:
checkBox.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e){
// Изменение заголовка окна.
if(checkBox.isSelected())
{
setTitle("CheckBox is selected");
}
else
{
setTitle("CheckBox isn't selected");
}
}
});
В этом обработчике мы просто меняем заголовок окна программы.
Результат выполнения программы будет таким:
Swing: JRadioButton
Элемент управления JRadioButton предназначен для организации выбора только одного значения из нескольких возможных. Для этого несколько элементов JRadioButton объединяются в одну группу, которая работает как единое целое - если выбрать одну из радиокнопок, входящих в группу, то остальные радиокнопки из этой группы становятся невыбранными.
Вот пример кода:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyFrame
extends JFrame
implements ActionListener {
// Переменные для радиокнопок.
public JRadioButton r1, r2, r3;
// Переменная для группы радиокнопок.
public ButtonGroup bg = new ButtonGroup();
// Конструктор.
public MyFrame() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
// Устанавливаем размеры и расположение.
setLocation(400, 200);
setSize(200, 200);
setTitle("Title");
// Задаем контейнер для компонентов.
Container con = getContentPane();
con.setLayout(new FlowLayout());
// Создание радиокнопок.
r1 = new JRadioButton("Java");
r2 = new JRadioButton("C/C++");
r3 = new JRadioButton("C#");
// Добавление радиокнопок к контейнеру.
con.add(r1);
con.add(r2);
con.add(r3);
// Добавление радиокнопок в группу.
bg.add(r1);
bg.add(r2);
bg.add(r3);
// Указание обработчиков для радиокнопок.
r1.addActionListener(this);
r2.addActionListener(this);
r3.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
// Изменение заголовка окна в зависимости от выбранной радиокнопки.
setTitle(e.getActionCommand());
}
public static void main(String[] args) {
// Создание главного окна.
new MyFrame().setVisible(true);
}
public void windowClosing(WindowEvent ev) {
System.exit(0);
}
}
Обратите внимание на следующие моменты. Во-первых, мы объявили наш класс реализующим интерфейс ActionListener:
public class MyFrame
extends JFrame
implements ActionListener {
...
Это мы сделали для возможности добавления обработчика нажатия на наши радиокнопки.
Во-вторых, для объединения радиокнопок в группу мы объявили переменную bg типа ButtonGroup и все радиокнопки в эту группу добавили:
...
// Добавление радиокнопок в группу.
bg.add(r1);
bg.add(r2);
bg.add(r3);
...
В-третьих, мы для всех радиокнопок указали один и тот же обработчик:
...
// Указание обработчиков для радиокнопок.
r1.addActionListener(this);
r2.addActionListener(this);
r3.addActionListener(this);
...
Этот обработчик - не что иное как метод actionPerformed:
public void actionPerformed(ActionEvent e) {
// Изменение заголовка окна в зависимости от выбранной радиокнопки.
setTitle(e.getActionCommand());
}
Этот метод из интерфейса ActionListener, для которого наш класс является потомком и который мы реализуем. Что за кнопку мы выбрали, мы определяем через метод getActionCommand для передаваемого параметра типа ActionEvent.
Запускаем программу. При выборе любой радиокнопки ее текст появится в заголовке окна:
Swing: Несколько групп радиокнопок
Ранее мы рассмотрели работу с одной группой радиокнопок. Сейчас же мы с вами посмотрим, как работать с несколькими группами радиокнопок.
Приведем сразу листинг программы:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyFrame
extends JFrame
implements ActionListener {
// Переменные для радиокнопок.
public JRadioButton r1, r2, r3, r4, r5;
String sel0, sel1; //Текст выбранной радиокнопки в каждой группе.
// Две переменные для группы радиокнопок.
public ButtonGroup bg = new ButtonGroup();
public ButtonGroup bg1 = new ButtonGroup();
// Конструктор.
public MyFrame() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocation(400, 200);
setSize(200, 200);
setTitle("Title");
Container con = getContentPane();
con.setLayout(new FlowLayout());
// Создание радиокнопок.
r1 = new JRadioButton("Java");
r2 = new JRadioButton("C/C++");
r3 = new JRadioButton("C#");
// Добавление радиокнопок к контейнеру.
con.add(r1);
con.add(r2);
con.add(r3);
// Добавление радиокнопок в группу.
bg.add(r1);
bg.add(r2);
bg.add(r3);
// Указание обработчиков для радиокнопок.
r1.addActionListener(this);
r2.addActionListener(this);
r3.addActionListener(this);
// Выделение первой радиокнопки в первой группе.
r1.setSelected(true);
// Создание радиокнопок.
r4 = new JRadioButton("Windows");
r5 = new JRadioButton("Linux");
// Добавление радиокнопок к контейнеру.
con.add(r4);
con.add(r5);
// Добавление радиокнопок в группу.
bg1.add(r4);
bg1.add(r5);
// Указание обработчиков для радиокнопок.
r4.addActionListener(this);
r5.addActionListener(this);
// Выделение первой радиокнопки во второй группе.
r4.setSelected(true);
// Задание первоначального заголовка окна.
sel0 = r1.getText();
sel1 = r4.getText();
setTitle(sel0 + " " + sel1);
}
public void actionPerformed(ActionEvent e) {
// Изменение заголовка окна в зависимости от выбранной радиокнопки.
String s = e.getActionCommand();
// Выясняем изменения в первой группе радиокнопок.
if(s==r1.getText() || s==r2.getText() || s==r3.getText()){
sel0 = s;
}
// Выясняем изменения во второй группе радиокнопок.
if(s==r4.getText() || s==r5.getText()){
sel1 = s;
}
setTitle(sel0 + " " + sel1);
}
public static void main(String[] args) {
// Создание главного окна.
new MyFrame().setVisible(true);
}
public void windowClosing(WindowEvent ev) {
System.exit(0);
}
}
Отличия тут от предыдущего листинга следующие - во-первых, мы добавили еще несколько радиокнопок (r4 и r5), добавили для них группу (bg1). Это мы делали точно также, как и ранее. Кроме того, мы сразу в конструкторе выделили первые радиокнопки в каждой группе (через вызов метода setSelected).
Во-вторых, так как у нас две группы, а обработчик для них один, то мы завели в классе две переменные sel0 и sel1 типа String для каждой группы. Каждая из этих переменных просто хранит текст выбранной радиокнопки из каждой группы.
В-третьих, мы в методе actionPerformed выясняем изменения в первой и второй группах радиокнопок и устанавливаем соответствующий заголовок окна.
Наша программа в работе будет выглядеть так, как и ожидалось - в заголовке окна будут показаны одновременно тексты выбранных радиокнопок из каждой группы:
Swing: Список JList
Класс JList предназначен для выбора пользователем одного или нескольких значений из списка. В этом элементе пользовательского интерфейса можно выбирать как один элемент, так и несколько.
Вот пример применения JList:
import javax.swing.*;
public class MyFrame extends JFrame {
private String [] s = {"red", "yellow", "green"};
public JList list;
public MyFrame(){
JPanel panel = new JPanel();
list = new JList(s);
panel.add(list);
setContentPane(panel);
setLocation(400, 200);
setSize(200, 200);
}
public static void main(String[] args) {
new MyFrame().setVisible(true);
}
}
А вот как это будет выглядеть:
Логика приведенного фрагмента достаточно проста: заполняем наш список данными из массива строк (в параметре конструктора), затем добавляем нас список к панели.
Простой графический редактор
import java.awt.*;
import java.awt.event.*;
public class DrawCanvas extends Canvas {
private int lastX, lastY;
private int ex, ey;
private int R=0,G=0,B=0;
private boolean clear=false;
public DrawCanvas () {
super();
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
switch(e.getModifiers())
{
case InputEvent.BUTTON1_MASK:
{R=255;G=0;B=0;}
break;
case InputEvent.BUTTON2_MASK:
{R=0;G=255;B=0;}
break;
case InputEvent.BUTTON3_MASK:
{R=0;G=0;B=255;}
break;
}
lastX = e.getX();
lastY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
ex=e.getX();
ey=e.getY();
repaint();
}
});
addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
if (e.getKeyChar()==' ') {
clear = true;
repaint();
}
}
});
}
public void update(Graphics g) {
if (clear) {
g.clearRect(0, 0, getWidth(), getHeight());
clear = false;
} else {
g.setColor(new Color(R,G,B));
g.drawLine(lastX, lastY, ex, ey);
lastX=ex;
lastY=ey;
}
}
public static void main(String s[]) {
final Frame f = new Frame("Draw");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
f.dispose();
}
});
f.setSize(400, 300);
final Canvas c = new DrawCanvas();
f.add(c);
f.setVisible(true);
}
}
Интерфейсы
Интерфейсы в Java предназначены только для объявления некоторых методов и констант. Никакие реализации методов в интерфейсах не предусматриваются. Т. е. интерфейс только содержит информацию о том, что методы с такими-то названиями в мнем существуют, но не содержит информации, как именно эти методы работают. Реализация же методов интерфейса будет содержаться в классах, которые этот интерфейс реализуют.
Интерфейсы вводятся ключевым словом interface. В объявлении же класса, реализующего некоторый интерфейс, после имени класса идет ключевое слово implements, после которого следует имя интерфейса или интерфейсов (класс может реализовывать несколько интерфейсов).
Вот примеры интерфейса для геометрической фигуры и 3-х классов конкретных геометрических фигур, реализующих этот интерфейс:
public interface IFigure {
double pi = 3.1415;
// Площадь.
double square();
}
// Класс прямоугольника.
public class Rect implements IFigure {
int width = 0;
int height = 0;
Rect(int width, int height){
this.width = width;
this.height = height;
}
public double square(){
return width * height;
}
}
// Класс треугольника.
public class Triangle implements IFigure {
double leg1 = 0;
double leg2 = 0;
public Triangle(double leg1, double leg2){
this.leg1 = leg1;
this.leg2 = leg2;
}
public double square() {
return leg1 * leg2 * 0.5;
}
}
// Класс круга.
public class Circle implements IFigure {
double radius;
public Circle(double radius){
this.radius = radius;
}
public double square() {
return radius * radius * pi;
}
}
А вот использование указанного интерфейса и классов, его реализующих:
public class Test {
public static void main(String[] args) {
Rect r = new Rect(2, 4);
System.out.println("Rect: " + r.square());
Triangle t = new Triangle(3, 4);
System.out.println("Triangle: " + t.square());
Circle c = new Circle(1);
System.out.println("Circle: " + c.square());
}
}
Обратите внимание, что каждый класс содержит метод square для вычисления площади. Этот метод обязан быть в каждом классе, так как он есть в интерфейсе, от которых классы наследуются. Также обратите внимание, что в классе Circle мы используем константу pi из интерфейса. В других языках программирования объявление констант в интерфейсах не допускается.
Выведет программа естественно Rect: 8.0 Triangle: 6.0 и Circle: 3.1415.
Проект «Подбор цвета»
import java.awt.*;
import java.awt.event.*;
public class ScrollTest extends Frame
{
Scrollbar sbRed = new Scrollbar(Scrollbar.VERTICAL, 127, 10, 0, 255);
Scrollbar sbGreen = new Scrollbar(Scrollbar.VERTICAL, 127, 10, 0, 255);
Scrollbar sbBlue = new Scrollbar(Scrollbar.VERTICAL, 127, 10, 0, 255);
//Scrollbar(int orientation, int value, int visible, int min, int max)
//задает ориентацию, начальное значение value, размер блока visible,
//диапазон значений min—max.
Label lblRGB = new Label();
Button btnPrim = new Button("Применить");
Button btnOtm = new Button("Отменить");
Button btnExit = new Button("Выход");
ScrollTest(String s){ super(s);
setLayout(null);
setFont(new Font("Serif", Font.BOLD, 15));
Panel p = new Panel();
p.setLayout(null);
p.setBounds(10,50, 150, 260); add(p);
Label lblColor = new Label("Подберите цвет");
lblColor.setBounds(20, 0, 120, 30); p.add(lblColor);
Label lblMin = new Label("0", Label.RIGHT);
lblMin.setBounds(0, 30, 30, 30); p.add(lblMin);
Label lblMiddle = new Label("127", Label.RIGHT);
lblMiddle.setBounds(0, 120, 30, 30); p.add(lblMiddle);
Label lblMax = new Label("255", Label.RIGHT);
lblMax.setBounds(0, 200, 30, 30); p.add(lblMax);
sbRed.setBackground(Color.red);
sbRed.setBounds(40, 30, 20, 200); p.add(sbRed);
sbGreen.setBackground(Color.green);
sbGreen.setBounds(70, 30, 20, 200); p.add(sbGreen);
sbBlue.setBackground(Color.blue);
sbBlue.setBounds(100, 30, 20, 200); p.add(sbBlue);
Label lblObraz = new Label("Образец:");
lblObraz.setBounds(250, 50, 120, 30); add(lblObraz);
lblRGB.setBackground(new Color(127, 127, 127));
lblRGB.setBounds(220, 80, 120, 80); add(lblRGB);
btnPrim.setBounds(230, 180, 100, 30); add(btnPrim);
btnOtm.setBounds(230, 220, 100, 30); add(btnOtm);
btnExit.setBounds(230, 260, 100, 30); add(btnExit);
setSize(400, 300); setVisible(true);
// Добавление обработчика для кнопки Применить
btnPrim.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
lblRGB.setBackground(new Color(sbRed.getValue(),sbGreen.getValue(),sbBlue.getValue()));
}
});
btnOtm.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
lblRGB.setBackground(new Color(127,127,127));
}
});
btnExit.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
System.exit(0);
}
});
}
public static void main(String[] args){
Frame f = new ScrollTest("Выбор цвета");
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent ev) {
System.exit(0);
}
});
}
}
Проект «Кардиоида»
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Panel extends JPanel{
public int x1,y1,x2,y2;
public Panel(){
setLayout(null);
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
int a=100;
double r=a*(1+Math.sin(0));
int x0=250, y0=250;
x2=(int) (x0+r*Math.cos(0));
y2=(int) (y0-r*Math.sin(0));
for(double i=1;i<=360;i++){
double rad=Math.PI*i/180;
r=a*(1+Math.sin(rad));
x1=(int) (x0+r*Math.cos(rad));
y1=(int) (y0-r*Math.sin(rad));
g.drawLine(x1,y1,x2,y2);
x2=x1;
y2=y1;
}
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class program extends JFrame{
private Panel pane = new Panel();
private JButton start = new JButton("Paint");
public program(){
JFrame mf = new JFrame("Paint Test");
mf.setLayout(new BorderLayout());
mf.setSize(500, 500);
mf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pane.setBorder(BorderFactory.createLineBorder(Color.BLACK));
pane.setBackground(Color.WHITE);
mf.add(pane, BorderLayout.CENTER);
mf.add(start, BorderLayout.SOUTH);
start.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){pane.repaint();}});
mf.setVisible(true);
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){new program();}
});
}
}
Менеджеры компоновок
Применение компоновок
При проектировании интерфейса пользователя с использованием языка Java компоненты размещаются в контейнерах. Самым простым примером контейнера может служить окно формы (класс Frame). В общем случае любой класс, наследуемый от класса контейнера java.awt.Container, является контейнером.
В языке Java для "плавающего" размещения компонентов, зависящего от размеров окна и размеров самих компонентов, введены классы компоновок.
Компоновка определяет порядок расположения компонентов на экране. Перед отображением объектов-контейнеров, содержащих другие компоненты, вызывается менеджер компоновки. Он располагает компоненты на экране в соответствующем порядке. Используемый менеджер компоновки указывается вызовом метода setLayout. Если менеджер компоновки не указан явно, то выбирается тот, что используется по умолчанию для данного класса контейнера.
Для того чтобы отключить использование менеджеров компоновки и перейти к явному указанию координат, следует вызвать метод setLayuot(null).
Пакеты java.awt и javax.swing предоставляют следующие классы менеджеров компоновки:
java.awt.FlowLayout - создает последовательное размещение объектов с указанным выравниванием и заданными между этими объектами интервалами.
java.awt.GridLayout - создает табличное размещение объектов с заданным числом строк и столбцов, объекты располагаются последовательно слева направо.
java.awt.GridBagLayout - создает гибкое табличное размещение объектов, позволяя размещать один компонент в нескольких ячейках.
java.awt.BorderLayout - создает размещение объектов по краю контейнера (вверху, внизу, слева, справа и в центре).
java.awt.CardLayout - создает компоновку контейнера, отображая одновременно только один компонент. Как правило, в качестве компонент для данного менеджера компоновки выступают панели.
javax.swing.ScrollPaneLayout - позволяет размещать компоненты на девяти различных областях; является компоновкой по умолчанию для класса ScrollPane.
javax.swing.ViewportLayout - является компоновкой по умолчанию для класса Jviewport.
javax.swing.BoxLayout - является компоновкой по умолчанию для класса Box.
javax.swing.OverlayLayout - позволяет размещать компоненты один над другим.
Для использования конкретного менеджера компоновки следует создать объект данного класса и установить созданную компоновку для данного контейнера, а затем добавить в контейнер другие компоненты.
Например:
GridBagLayout gridBagLayout1 = new GridBagLayout();
this.setLayout(gridBagLayout1);
Button button1 = new Button();
button1.setLabel("Кнопка 1");
this.add(button1,
new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0,
GridBagConstraints.CENTER,
GridBagConstraints.NONE,
new Insets(0, 0, 0, 0),
315, 0));
Метод setLayout следует квалифицировать именем контейнера, для которого устанавливается компоновка. Для класса контейнера, в котором выполняется данный метод, можно использовать ссылку this. В случае предварительного создания объекта "менеджер компоновки", его следует указать как параметр метода setLayout. В противном случае в качестве параметра метода setLayout следует указать оператор создания анонимного объекта "менеджер компоновки".
Например:
this.setLayout(gridLayout1);
// или
this.setLayout(new GridLayout());
При использовании панели типа Panel методы объекта "менеджер компоновки" вызываются стандартным образом имя_объекта_менеджер_компоновки.имя_метода.
Например:
cardLayout1.show(Panel1,"Panel0");.
При использовании панели типа JPanel методы объекта "менеджер компоновки" вызываются с использованием метода getLayout.
Например:
((CardLayout)jPanel2.getLayout()).show(jPanel2,"jPanel0");
Классы компоновок
Класс BorderLayout
Менеджер компоновки BorderLayout разбивает контейнер на пять областей и располагает добавляемые в контейнер объекты по краям (север, юг, запад, восток) и в центре.
Каждая область указывается соответствующей константой: NORTH, SOUTH, EAST, WEST и CENTER. Если в методе add отсутствует строка, указывающая расположение компонента, то по умолчанию используется значение CENTER.
На рисунке приведен внешний вид, реализуемый менеджером компоновки BorderLayout для пяти кнопок, которые расположены в контейнере - апплете.
Следующий код иллюстрирует использование компоновки BorderLayout:
import java.applet.Applet;
import java.awt.*;
public class MyApplet extends Applet {
public MyApplet() {
try { jbInit();}
catch(Exception e) { } }
public static void main(String[] args) {
MyApplet myApplet1 = new MyApplet(); }
private void jbInit() throws Exception {
setLayout(new BorderLayout());
add(new Button("North"), BorderLayout.NORTH);
add(new Button("South"), BorderLayout.SOUTH);
add(new Button("East"), BorderLayout.EAST);
add(new Button("West"), BorderLayout.WEST);
add(new Button("Center"), BorderLayout.CENTER);
}
}
Класс BorderLayout предоставляет ряд методов, включая следующие:
GetHgap - возвращает расстояние в пикселях между компонентами по горизонтали.
SetHgap - устанавливает расстояние в пикселях между компонентами по горизонтали.
GetVgap - возвращает расстояние в пикселях между компонентами по вертикали.
SetVgap - устанавливает расстояние в пикселях между компонентами по вертикали.
Класс CardLayout
Класс CardLayout определяет менеджер компоновки для контейнера, который может содержать несколько страниц ("карт") и для которого одновременно может быть видна только одна карта.
Класс CardLayout предоставляет ряд методов, включая следующие:
GetHgap- определяет отступ по горизонтали.
GetVgap - определяет отступ по вертикали.
First - активизирует первую страницу контейнера.
Last - активизирует последнюю страницу контейнера.
Next - активизирует следующую страницу контейнера в циклическом порядке (после последней карты активизируется первая карта).
Previous - активизирует предыдущую страницу контейнера в циклическом порядке.
Show - активизирует компонент указанного контейнера.
Например:
// Для контейнера типа JPanel
void jButton1_actionPerformed(ActionEvent e) {
((CardLayout)jPanel1.getLayout()).next(jPanel1);
}
// Для контейнера типа Panel
void button1_actionPerformed(ActionEvent e) {
cardLayout1.next(panel1);
}
Класс FlowLayout
Менеджер компоновки FlowLayout размещает добавляемые в контейнер компоненты последовательно слева направо. Компоненты могут быть размещены в нескольких последовательных рядах.
На рисунке приведены два результата применения этой компоновки при изменении размеров контейнера.
Класс FlowLayout предоставляет следующие константы, определяющие выравнивание компонентов:
CENTER - по центру.
LEFT - по левому краю.
RIGHT - по правому краю.
Класс FlowLayout предоставляет ряд методов, включая следующие:
SetAlignment - устанавливает выравнивание компонентов для данной компоновки. Параметр метода может принимать следующие значения: FlowLayout.LEFT, FlowLayout.RIGHT и FlowLayout.CENTER.
GetHgap - определяет расстояние между компонентами по горизонтали.
SetHgap - устанавливает расстояние между компонентами по горизонтали.
GetVgap - определяет расстояние между компонентами по вертикали.
SetVgap- устанавливает расстояние между компонентами по вертикали.
Класс GridLayout
Этот класс позволяет размещать компоненты в контейнере в виде таблицы. В каждой ячейке таблицы может быть размещен только один компонент.
Размер всех ячеек таблицы одинаков. Количество строк и столбцов таблицы определяется или в конструкторе, или вызовом методов setColumns и setRows. При этом, если эти значения не равны нулю, то количество столбцов является величиной вычисляемой и зависит от общего числа компонентов, добавленных на компоновку, и указанного числа строк. И только в том случае, если количество строк задано равным нулю, заданное количество столбцов будет учитываться менеджером компоновки.
На рисунке приведены примеры использования компоновки GridLayout.
Для компоновки GridLayout следует определять или количество строк, или количество столбцов.
Например:
this.setLayout(gridLayout1);
gridLayout1.setRows(3);
Класс GridBagLayout
Этот класс используется менеджером компоновки GridBagLayout и определяет требования к размещению компонентов.
Компоновка GridBagLayout позволяет размещать компоненты в ячейках таблицы. Но, в отличие от менеджера компоновки GridLayout, ячейки таблицы могут различаться как по ширине, так и по высоте. Размещаемые компоненты могут занимать несколько ячеек.
Область, занимаемая компонентом, называется областью отображения. Ее размеры определяются значениями переменных gridwidth и gridheight (количество ячеек по горизонтали и по вертикали) класса GridBagConstraints.
Отступами (insets) называется расстояние между областью отображения и действительной областью, занимаемой компонентом.
На рисунке приведен пример компоновки, в которой кнопка 3 занимает 9 ячеек, но размер кнопки меньше размера области отображения.
Если область отображения отличается от размера компонента, то для определения требований к размещению используется переменная fill.
Если размер размещаемого компонента меньше размера области отображения, то для указания размещения компонента используется переменная anchor, которая может принимать одно из следующих значений:
GridBagConstraints.CENTER
GridBagConstraints.NORTH
GridBagConstraints.NORTHEAST
GridBagConstraints.EAST
GridBagConstraints.SOUTHEAST
GridBagConstraints.SOUTH
GridBagConstraints.SOUTHWEST
GridBagConstraints.WEST
GridBagConstraints.NORTHWEST.
Переменная fill класса GridBagConstraint определяет, следует ли изменять размер компонента, и может принимать следующие значения:
GridBagConstraint.NONE - размер компонента не изменять (значение, используемое по умолчанию);
GridBagConstraint.HORIZONTAL - изменить размер по горизонтали, но не изменять его высоту;
GridBagConstraint.VERTICAL - изменить размер по вертикали, но не изменять его ширину;
GridBagConstraint.BOTH - увеличить размеры компонента до размера области отображения.
Переменные gridheight и gridwidthкласса GridBagConstraint определяют число ячеек в столбце или строке соответственно. При этом константа GridBagConstraints.REMAINDER указывает, что компонент будет последним в столбце (строке), а константа GridBagConstraints.RELATIVE указывает, что компонент будет ближайшим к последнему.
Конструктор GridBagConstraints(int gridx, int gridy, int gridwidth, int gridheight, double weightx, double weighty, int anchor, int fill, Insets insets, int ipadx, int ipady) создает объект требований к размещению компонента, используемый менеджером компоновки, со всеми полями, имеющими заданные значения.
Компоненты графического интерфейса пользователя
Панели
Java-приложение создается как иерархия вложенных компонентов. Наверху этой иерархии могут находится компоненты классов Frame, JFrame, Applet или JApplet.
Классы панелей используются как контейнеры для размещения других компонентов, в числе которых могут быть и сами панели. Пакет оконного интерфейса java.awt содержит один класс панели - Panel. Для добавления компонентов на панель класса Panel следует выполнить метод add, квалифицировав его именем панели.
Пакет облегченных swing-компонентов предоставляет несколько классов панелей, включая следующие классы:
JRootPane
JPanel
JTabbedPane
JScrollPane
JSplitPane
JOptionPane
JToolbar.
При использовании менеджеров компоновки с панелями из пакета java.swing для получения ссылки на панель следует использовать метод getLayout.
Например:
((CardLayout)jPanel1.getLayout()).next(jPanel1);
Пакет javax.swing содержит интерфейс RootPaneContainer, реализуемый классами JApplet, JFrame, JInternalFrame, JWindow и JDialog.
При добавлении компонентов в контейнер, использующий интерфейс RootPaneContainer , метод add надо квалифицировать следующим образом:
frame.getContentPane().add(child).
Класс JRootPane используется всеми панелями пакета javax.swing. Он позволяет размещать содержимое панели на нескольких уровнях. JRootPane представляет четыре уровня:
ContentPane - используется для отображения компонентов.
MenuBar - необязательный уровень, на котором можно размещать компоненты меню.
LayeredPane - используется для управления contentPane и menuBar.
GlassPane - самый ближайший к пользователю уровень. По умолчанию он невидим. glassPane, как правило, используется для отображения графики или каких-либо всплывающих компонентов. Компоненты этого уровня перекрывают компоненты, расположенные на более "низких" уровнях.
Уровень contentPane должен быть родительским для размещения любого компонента. Для доступа к данному уровню используется метод getContentPane().
Уровень glassPane является самым верхним. Для доступа к данному уровню применяется метод getGlassPane(). Чтобы в приложении можно было использовать уровень glassPane, для него следует вызвать метод setGlassPane().
Например:
panel1.getContentPane().add(button1);
Компонент JTabbedPane реализует набор страниц, переключаемых по щелчку пользователя на вкладках. Вкладки добавляются методами addTab и insertTab. Вкладки нумеруются, начиная с 0.
Если количество вкладок равно нулю, то индекс текущей вкладки равен -1.
На рисунке показан внешний вид окна, содержащего панель вкладок класса JTabbedPane.
Класс javax.swing.JScrollPane реализует компонент "прокручиваемая область".
Панель JScrollPane состоит из:
области просмотра ( viewport) типа JViewport. Для добавления рамки вокруг области просмотра следует использовать метод setViewportBorder, а для добавления рамки вокруг всей прокручиваемой области - метод setBorder.;
вертикальной и горизонтальной области прокрутки типа JScrollBar (необязательная область);
области заголовков строк и области заголовкоd столбцов (необязательная область). Эти области реализуются как объекты типа JViewport, для которых вызваны методы setRowHeaderView и setColumnHeaderView;
четырех угловых областей. По умолчанию угловые области не содерджaт компонентов. Для того чтобы разместить в угловой области компонент, следует использовать метод setCorner.
Чтобы изменить свойство области просмотра, ее следует получить, вызвав метод getViewport. Например, для задания цвета фона нужно записать:
scrollPane.getViewport().setBackground(Color.red).
На рисунке показано расположение основных частей прокручиваемой области JScrollPane.
Контейнером для разделяемых панелей служит компонент JSplitPane. Разделяемые плавающие панели имеют одну общую сторону - по вертикали или по горизонтали.
Следующий пример иллюстрирует создание окна, в котором применяются разделяемые панели:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Frame1 extends JPanel {
JSplitPane jSplitPane1 = new JSplitPane(); // Класс
// разделяемых панелей
JScrollPane jScrollPane1 = new JScrollPane();
JScrollPane jScrollPane2 = new JScrollPane();
JTextPane jTextPane1 = new JTextPane();
JTextPane jTextPane2 = new JTextPane();
public Frame1() { try { jbInit(); }
catch(Exception e) { } }
public static void main(String s[]) {
JFrame frame = new JFrame("Панели");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0); } });
frame.setContentPane(new Frame1());
frame.pack();
frame.setVisible(true);
}
private void jbInit() throws Exception {
// Определение ориентации разделяемых панелей
jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
// Размещение левого и правого компонента
// на разделяемой панели
jSplitPane1.setLeftComponent(jScrollPane1);
jSplitPane1.setRightComponent(jScrollPane2);
// Отображение кнопок сворачивания и разворачивания
// сторон разделяемой панели
jSplitPane1.setOneTouchExpandable(true);
// Задание размера панелей
jScrollPane1.setPreferredSize(new Dimension(300, 60));
jScrollPane2.setPreferredSize(new Dimension(300, 60));
// Добавление разделяемой панели к окну формы
this.add(jSplitPane1, null);
// Добавление компонентов в контейнеры
// типа JScrollPane
jScrollPane1.getViewport().add(jTextPane1, null);
jScrollPane2.getViewport().add(jTextPane2, null);
}
}
Кнопки
Кнопки могут располагаться в контейнере как отдельно, так и в группе.
Пакет java.awt содержит следующие классы кнопок и групп:
Button - командная кнопка.
Checkbox - флажок или радиокнопка (переключатель).
CheckboxGroup - группа кнопок.
Пакет облегченных swing-компонентов также предоставляет несколько классов кнопок и групп, включая следующие классы:
JButton - кнопка.
JRadioButton - радиокнопка.
JToggleButton - кнопка-переключатель.
JCheckBox - флажок
ButtonGroup - группа кнопок.
На кнопках из пакета javax.swing помимо метки может также отображаться и пиктограмма.
Класс Button позволяет создавать объекты "командные кнопки". При нажатии на кнопку JVM инициирует событие действия actionPerformed. Наряду с данным семантическим событием, инициируется ряд простых событий, таких как mouseClicked.
Класс Button предоставляет ряд методов, включая следующие:
AddActionListener - регистрирует блок прослушивания для события действия от командной кнопки;
GetActionCommand - возвращает имя команды, инициировавшей событие действия, или метку кнопки, если имя команды равно null (значение по умолчанию);
GetLabel - возвращает метку командной кнопки или null для непомеченной командной кнопки;
SetLabel - изменяет метку командной кнопки.
Класс Checkbox позволяет создавать компоненты кнопки двух типов, называемые флажками и радиокнопками. Такой компонент может иметь два состояния: включено (true) и выключено (false). Каждый щелчок на компоненте изменяет его состояние на обратное.
Несколько переключателей могут быть объединены в группу, используя компонент CheckboxGroup, являющийся контейнером. Такая группа называется группой радиокнопок. Только один компонент группы может одновременно иметь состояние "включен".
Для обработки событий от кнопки используется интерфейс ItemListener. Этот интерфейс определяет всего один обработчик события - метод itemStateChanged (ItemEvent e).
Класс ItemEvent содержит метод getStateChange, позволяющий определить состояние кнопки. Метод может возвращать одно из двух возможных значений:
ItemEvent.SELECTED;
ItemEvent.DESELECTED.
Например:
// Определение состояния кнопки
void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED)
{ // Кнопка находится во включенном состоянии
}
}
Для добавления кнопки в группу для нее следует вызвать метод setCheckboxGroup, указав в качестве параметра объект типа CheckboxGroup.
Следующий листинг иллюстрирует создание флажков и группы радиокнопок:
import java.applet.Applet;
import java.awt.*;
public class MyApplet extends Applet {
Panel panel1 = new Panel();
GridLayout gridLayout1 = new GridLayout();
// Создание новой кнопки:
Checkbox checkbox1 = new Checkbox();
Checkbox checkbox2 = new Checkbox();
GridLayout gridLayout2 = new GridLayout();
Panel panel2 = new Panel();
// Создание группы кнопок:
CheckboxGroup checkboxGroup1 = new CheckboxGroup();
Checkbox checkbox4 = new Checkbox();
Checkbox checkbox5 = new Checkbox();
Checkbox checkbox6 = new Checkbox();
FlowLayout FlowLayout1 = new FlowLayout();
public MyApplet() {
try { jbInit(); } catch(Exception e) { }
}
public static void main(String[] args) {
MyApplet myApplet1 = new MyApplet(); }
private void jbInit() throws Exception {
setLayout(gridLayout2);
panel1.setLayout(gridLayout1);
checkbox1.setLabel("Флажок 1");
checkbox2.setLabel("Флажок 2");
checkbox2.setState(true); // Состояние флажка -
// включен
gridLayout2.setRows(2);
panel2.setLayout(FlowLayout1);
// Добавление в группу трех кнопок:
checkbox4.setCheckboxGroup(checkboxGroup1);
checkbox4.setLabel("Радиокнопка 1");
checkbox4.setState(true); // Состояние радиокнопки -
// включена
checkbox5.setCheckboxGroup(checkboxGroup1);
checkbox5.setLabel("Радиокнопка 2");
checkbox6.setCheckboxGroup(checkboxGroup1);
checkbox6.setLabel("Радиокнопка 3");
// Добавление кнопок в контейнеры - панели
this.add(panel1, null);
panel1.add(checkbox1, null);
panel1.add(checkbox2, null);
this.add(panel2, null);
panel2.add(checkbox4, null);
panel2.add(checkbox5, null);
panel2.add(checkbox6, null);
}
}
Классы кнопок пакета javax.swing наследуются от класса AbstractButton. Этот класс предоставляет ряд общих методов, включая следующие:
doClick - выполнение щелчка мыши на кнопке программным способом;
getText- возвращает метку кнопки;
setText - устанавливает метку кнопки;
isSelected - определяет состояние кнопки и возвращает true, если переключаемая кнопка находится в состоянии "включена";
setSelected - устанавливает состояние кнопки;
setMargin - устанавливает отступы между рамкой кнопки и меткой;
setIcon - определяет пиктограмму, используемую по умолчанию. Эта пиктограмма используется для состояния кнопки "нажатая" или "недоступная" в том случае, если для этих состояний не указана иная пиктограмма;
setPressedIcon- определяет пиктограмму для нажатого ("pressed") состояния кнопки;
setSelectedIcon - устанавливает пиктограмму для выделенного ("selected") состояния кнопки;
setRolloverIcon - устанавливает пиктограмму для нажатого ("rollover") состояния кнопки (одновременно могут быть нажаты несколько кнопок);
setDisabledIcon- устанавливает пиктограмму для недоступного состояния кнопки.
Например:
ImageIcon ImageIcon1= new
ImageIcon("myicon.gif");
jButton1.setDisabledIcon(ImageIcon1);
setDisabledSelectedIcon - устанавливает пиктограмму недоступного для выделения состояния кнопки;
setVerticalAlignment - определяет выравнивание по вертикали для пиктограммы и метки, которое может указываться следующими значениями:
SwingConstants.CENTER (по умолчанию);
SwingConstants.TOP;
- SwingConstants.BOTTOM;
setHorizontalAlignment - определяет выравнивание по горизонтали для пиктограммы и метки, которое может указываться следующими значениями:
SwingConstants.RIGHT (по умолчанию);
SwingConstants.LEFT;
SwingConstants.CENTER;
SwingConstants.LEADING;
SwingConstants.TRAILING;
setVerticalTextPosition - определяет позицию текста по вертикали относительно пиктограммы, которая может указываться следующими значениями:
SwingConstants.CENTER (по умолчанию);
SwingConstants.TOP;
SwingConstants.BOTTOM;
setHorizontalTextPosition - определяет позицию текста по горизонтали относительно пиктограммы, которая может указываться следующими значениями:
SwingConstants.RIGHT (по умолчанию);
SwingConstants.LEFT;
SwingConstants.CENTER;
SwingConstants.LEADING;
SwingConstants.TRAILING.
setActionCommand - задает команду действия для кнопки;
getActionCommand - возвращает строку, которая содержит команду действия, установленную для данной кнопки;
setMnemonic - определяет код символа, используемого как ключ-акселератор (ALT+данный символ).
Например:
jButton1.setMnemonic(KeyEvent.VK_A);
Для того чтобы определить в методе обработки события itemStateChanged(ItemEvent e), какой кнопкой было инициировано данное событие, следует использовать метод getItemSelectable.
Для того чтобы определить, является ли кнопка, инициировавшая событие, выделенной (находящейся в состоянии "включена" или "нажата"), следует использовать метод getStateChange.
Например:
if (e.getItemSelectable() ==
jCheckBox1 && e.getStateChange()==e.SELECTED) {
}
Класс JToggleButton предназначен для создания кнопок, имеющих два состояния - "нажата" и "не нажата". Компонент JToggleButton может находится в зафиксированном нажатом состоянии (быть выделенным).
Этот класс является непосредственным суперклассом для классов JCheckbox и JRadioButton.
Для того чтобы установить состояние кнопки, следует использовать метод setSelected .
Например:
jToggleButton1.setSelected(true);
Кнопки JToggleButton могут быть объединены в группу. Такую группу следует предварительно создать, используя компонент типа javax.swing.ButtonGroup. Только одна из кнопок группы может одновременно иметь нажатое состояние.
Текстовые компоненты
Текстовые компоненты предназначаются для ввода и отображения строк.
Библиотека JDK предоставляет следующие классы текстовых компонентов:
java.awt.TextArea - текстовая область.
java.awt.TextField - текстовое поле, называемое также полем ввода.
javax.swing.JTextField - текстовое поле.
javax.swing.JPasswordField - текстовое поле, предназначаемое для ввода пароля.
javax.swing.JTextArea - текстовая область.
javax.swing.JEditorPane - панель редактора, позволяющая отображать как текстовые, так и графические данные, а также применять различное форматирование текста.
javax.swing.JTextPane - текстовая панель, позволяющая отображать содержимое с использованием различных шрифтов.
Все текстовые компоненты пакета java.awt наследуются от класса TextComponent.
Все текстовые компоненты пакета javax.swing наследуются от класса JTextComponent.
Для отображения текста, который может быть изменен только программным путем, служат компоненты .Label из пакета java.awt и JLabel из пакета javax.swing.
Компонент JLabel также можно использовать и для отображения рисунков.
Большинство названий методов, предоставляемых классами TextComponent и JTextComponent для работы с текстом, совпадают. Так, для того чтобы получить строку, которая содержит весь текст, расположенный в текстовом компоненте, можно использовать метод getText, а для получения позиции ввода - метод getCaretPosition; для определения, можно ли редактировать текст, - метод isEditable, для выделения текста в указанном диапазоне - метод select, а для выделения всего текста - метод selectAll.
При каждом изменении значения текстового компонента, включая ввод каждого символа, происходит событие textValueChanged. При нажатии на клавишу Enter для текстового поля инициируется событие actionPerformed. Но при перемещении фокуса ввода событие actionPerformed не происходит.
Класс TextField позволяет создавать однострочные текстовые поля, используемые для ввода и редактирования одной строки текста. Этот класс предоставляет ряд методов, включая следующие:
AddActionListener - регистрирует блок прослушивания для обработки события действия компонента "текстовое поле".
EchoCharIsSet - возвращает значение true в том случае, если ввод в данное текстовое поле отображается некоторым эхо-символом, скрывающим действительное значение поля.
GetColumns - возвращает размер данного текстового поля (количество символов).
GetEchoChar - возвращает эхо-символ для данного текстового поля.
SetEchoCharacter - устанавливает значение эхо-символа для данного текстового поля. Любой вводимый пользователем символ будет экранирован данным эхо-символом.
SetColumns - устанавливает размер текстового поля в символах.
SetText - определяет новое значение текстового поля.
Хотя компоненты пакета javax.swing и называются облегченными, но они предоставляют значительно большие возможности, чем традиционные текстовые компоненты пакета java.awt.
Управление текстом, отображаемым в текстовом компоненте, определяется интерфейсом Document. Этот интерфейс предназначен для определения методов работы с текстом. Интерфейс Documentреализован классом AbstractDocument.
Текст в компоненте типа JTextComponent представляется ассоциируемой с ним текстовой моделью, определяющей как содержание, так и стиль.
Фактически, текстовый компонент предоставляет доступ к:
модели, реализуемой на основе интерфейса Document и представляющей собой содержание документа;
области просмотра текстового компонента;
набору команд редактирования, предусмотренного для текстового компонента;
таблице ключей акселераторов, связанных с таблицей именованных действий.
Класс DefaultEditorKit и StyledEditorKit описывают набор именованных действий, которые можно использовать для создания на основе текстового компонента редактора текста.
В классе DefaultEditorKit определен набор предоставляемых текстовому компоненту действий, таких как перемещение курсора, выделение или вставка текста.
Например, для создания пункта меню, выполняющего форматирование выделенного диапазона текста, и изменения размера шрифта на 14p, следует записать:
menu.add(new StyledEditorKit.FontSizeAction (
"Имя пункта меню", 14));
Константы класса DefaultEditorKit описывают набор именованных действий, включая следующие:
BackwardAction - перемещение позиции ввода на одну позицию назад.
BeepAction - подача звукового сигнала.
BeginAction - перемещение позиции ввода на начало документа.
BeginParagraphAction - перемещение позиции ввода в начало абзаца.
BeginLineAction - перемещение позиции ввода в начало строки.
BeginWordAction - перемещение позиции ввода на начало текущего слова.
CutAction - вырезание выделенного диапазона текста и помещение его в буфер обмена.
CopyAction - копирование выделенного диапазона текста в буфер обмена.
DeleteNextCharAction - удаление следующего символа.
DownAction- перемещение позиции ввода на один ряд вниз.
DeletePrevCharAction - удаление предыдущего символа.
EndAction - перемещение позиции ввода в конец документа.
EndLineAction - перемещение позиции ввода в конец строки.
EndParagraphAction - перемещение позиции ввода в конец абзаца.
EndWordAction - перемещение позиции ввода на конец текущего слова.
ForwardAction - перемещение позиции ввода на один символ вперед.
InsertBreakAction - вставка в документ символа конца абзаца.
InsertTabAction - вставка символа табуляции.
NextWordAction - перемещение позиции ввода на начало следующего слова.
PageDownAction - перемещение позиции ввода на одну страницу вниз.
PageUpAction - перемещение позиции ввода на одну страницу вверх.
PasteAction - вставка содержимого буфера обмена вместо выделенного диапазона текста или перед текущей позицией ввода.
PreviousWordAction - перемещение позиции ввода на начало предыдущего слова.
ReadOnlyAction - перевод редактора в режим "только чтение".
SelectAllAction - выделение всего документа.
SelectionBackwardAction - расширение области выделения на одну позицию назад (влево).
SelectionBeginAction - расширение области выделения до начала документа.
SelectionBeginLineAction- расширение области выделения до начала текущей строки.
SelectionBeginParagraphAction - расширение области выделения до начала текущего абзаца.
SelectionBeginWordAction - расширение области выделения до начала текущего слова.
SelectionDownAction - расширение области выделения на одну позицию вниз.
SelectionEndAction - расширение области выделения до конца документа.
SelectionEndLineAction - расширение области выделения до конца строки.
SelectionEndParagraphAction- расширение области выделения до конца абзаца.
SelectionEndWordAction - расширение области выделения до конца текущего слова.
SelectionForwardAction - расширение области выделения на один символ вперед (вправо).
SelectionNextWordAction - расширение области выделения до начала следующего слова.
SelectionPreviousWordAction - расширение области выделения до начала предыдущего слова.
SelectionUpAction - расширение области выделения на одну позицию вверх.
SelectLineAction - выделение строки, в которой расположена позиция ввода.
SelectParagraphAction - выделение абзаца, в котором расположена позиция ввода.
SelectWordAction - выделение слова, в котором расположена позиция ввода.
UpAction - перемещение позиции ввода на одну позицию вниз.
WritableAction - перевод редактора в режим редактирования текста.
Для того чтобы добавить в документ строку в отформатированном виде, можно использовать метод insertS класса DefaultStyledDocument. Этот класс является подклассом класса AbstractDocumentи реализует интерфейсы Document и StyledDocument.
Интерфейс StyledDocument определяет методы для работы со стилем документа. Так, для назначения диапазону текста набора атрибутов стиля можно использовать метод setCharacter-Attributes, а для определения используемого стиля или шрифта - методы getStyle и getFont.
Атрибуты стиля определяются интерфейсом AttributeSet. Этот интерфейс реализован классом SimpleAttributeSet.
Для создания набора атрибутов стиля следует создать переменную типа SimpleAttributeSet и установить атрибуты стиля, используя методы класса
Например:
SimpleAttributeSet attrSt = new SimpleAttributeSet();
StyleConstants.setBold(attrSt, true);
StyleConstants.setFontSize(attrSt, 12);
StyleConstants.setForeground(attrSt, Color.red);
При добавлении строки в текстовый документ вызовом метода insertString класса Default-StyledDocument один из параметров ссылается на набор аттрибутов.
Фактически набор атрибутов стиля накладывается на диапазон текста. Если текстовый компонент содержит текст с различным форматированием, то на каждый отдельный диапазон текста должен быть "наложен" свой набор атрибутов стиля.
Для создания таблицы ключей акселераторов, используемых для текстового компонента, следует:
Создать объект класса Keymap для компонента класса JTextPane.
Например:
JTextPane textPane;
....
Keymap keymap = textPane.addKeymap("MyKeymap",
textPane.getKeymap());
Создать объект класса Action и установить для него действие, описанное в классе DefaultEditorKit.
Например:
Action action = getActionByName(DefaultEditorKit.downAction);
Создать объект класса KeyStroke и установить для него значение ключа акселератора.
Например:
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_N,
Event.CTRL_MASK);
Установить связь между созданным действием и ключем акселератором.
Например:
keymap.addActionForKeyStroke(key, action);
Класс StyleConstants предоставляет набор методов, позволяющих определять или устанавливать значения для атрибутов форматирования.
Набор атрибутов форматирования может быть создан как объект одного из следующих классов:
SimpleAttributeSet .
AttributeSet .
MutableAttributeSet .
Например:
...SimpleAttributeSet[] attrs = new
SimpleAttributeSet[stringForPane.length +1];
// Создание набора атрибутов:
attrs[0] = new SimpleAttributeSet();
StyleConstants.setFontFamily(attrs[0], "SansSerif");
// Установка значения атрибута
StyleConstants.setFontSize(attrs[0], 12);
attrs[1] = new SimpleAttributeSet(attrs[0]);
StyleConstants.setFontSize(attrs[1], 14);
StyleConstants.setBold(attrs[1], true);
// Добавление строки в документ с заданным набором
// атрибутов (defaultStyleDocument1 переменная
// класса, наследуемого от DefaultStyleDocument)
defaultStyleDocument1.insertString(
defaultStyleDocument1.length,
string1,
attrs[1]);
Списки
Списки позволяют отображать группу элементов в один столбец, предоставляя пользователю возможность выбора элемента.
Библиотека JDK содержит ряд классов списков, включая следующие:
java.awt.List - список.
java.awt.Choice - ниспадающий список.
javax.swing.JList - список.
javax.swing.JComboBox - ниспадающий список.
При выделении элемента в списке или в ниспадающем списке (или отмене выделения) инициируется событие itemStateChanged. В метод обработки этого события передается объект типа ItemEvent. Используя свойство SELECTED для объекта типа ItemEvent, можно определить, выделен ли элемент списка. Интерфейс ItemListener описывает метод обработки события itemStateChanged.
При двойном щелчке мышью на элементе списка (или нажатии клавиши Enter при выделенном элементе списка) для компонента типа List, JList или JComboBox инициируется событие actionPerformed. В метод обработки этого события передается объект типа ActionEvent. Интерфейс ActionListener описывает метод обработки события actionPerformed.
Заполнение списков можно выполнять в обработчике события фрейма windowOpened. Добавление элементов в список List и Choice выполняется методом add.
Например:
List list1 = new List(4, false); // Создание списка
// с 4 видимыми строками и с запретом множественного выбора
list1.add("Строка 1"); // Добавление элементов в список
list1.add("Строка 2");
list1.add("Строка 3");
list1.add("Строка 4");
list1.add("Строка 5");
list1.add("Строка 6");
add(list1); // Добавление списка в текущий контейнер
Список JList позволяет выделять один или несколько элементов. Содержание списка представляется моделью ListModel. Доступ к элементам списка реализуется с использованием модели. Для заполнения списка используется метод setListData.
Список JList непосредственно не поддерживает прокрутку списка. Реализовать скроллинг можно двумя способами:
Поместить список в контейнер типа JScrollPane. Например:
JScrollPane scrollPane = new JScrollPane(jList1);
Установить для объекта типа JScrollPane на уровне Viewport (getViewport) в качестве компонента области просмотра (setView) объект типа JList. Например:
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport().(jList1);
Например:
String[] data = {"один", "два", "три"}; // Массив строк
JList jList1 = new JList(data); // Создание списка,
// содержащего массив строк
// Доступ к элементам списка через модель
for(int i = 0; i<jList1.getModel().getSize(); i++) {
System.out.println(jList1.getModel().getElementAt(i)); }
// Заполнение списка данными, представляемыми классом Vector
JList jList1 = new JList();
Vector superClasses = new Vector();
Class rootClass = javax.swing.JList.class; // Создание объекта
// типа Class
for(Class cls = rootClass; cls != null;
cls = cls.getSuperclass())
{ // Получение всех подклассов
superClasses.addElement(cls); }
jList1.setListData(superClasses); // Заполнение компонента jList1
// списком всех его подклассов
// Добавление элементов в список, хранимый в объекте типа Vector
superClasses.addElement(new String("12345"));
// Выделение элементов списка:
jList1.setSelectedIndex(1); // Выделение второго элемента списка
jList1.getSelectedValue(); // Возвращает строку, отображаемую
//во втором элементе списка
Интерфейс в качестве типа
Мы не можем создать экземпляр интерфейса. Именно потому, что у него не существует реализации методов. Т. е. мы не можем написать, например, так:
IFigure f = new IFigure(); // Ошибка!
С другой стороны в переменную типа интерфейса мы можем записать любой класс, реализующий этот интерфейс. Например, в интерфейс IFigure из прошлой программы мы можем записать класс Rect:
IFigure f = new Rect(2, 4);
Это оказывается весьма удобным - так как реальный тип переменной нам может быть не известен на этапе компиляции. Известен же он может быть только на этапе выполнения программы.
Вот так может быть переделан тестовый класс из программы выше:
public class Test {
public static void main(String[] args) {
IFigure[] f = new IFigure[2];//= //new Rect(2, 4);
f[0] = new Rect(2, 4);
f[1] = new Circle(1);
for (int i = 0; i < 2; i++){
System.out.println("Square[" + i + "]: " + f[i].square());
}
}
}
Обратите внимание, что тут мы в переменные типа интерфейса (у нас массив из 2-х переменных такого типа) записываем классы-потомки этого интерфейса (а именно Rect и Circle). И в момент вызова метода square интерфейса фактически вызовется метод из соответствующего класса, реализующего этот интерфейс. Т. е. площадь для прямоугольника будет считаться по его формуле, а для круга - по его.
Множественное наследование
В Java, как и во многих других языках программирования, не допускается множественное наследование для классов. У класса может быть только один непосредственный предок (у которого, в свою очередь, может быть и свой один предок и т. п.). Множественное наследование в Java допускается только для интерфейсов. Т. е. в качестве второго (третьего и т. д.) предка может выступать только интерфейс. Комбинации тут возможны разные - например, несколько интерфейсов могут выступать в качестве предков как для класса, так и для интерфейса. Или интерфейсы могут быть предками совместно с одним классом.
Вот несколько примеров (в которых подразумевается, что интерфейсы IInterface1 и IInterface2 существуют):
// Интерфейс с 2-я интерфейсами-предками.
public interface IInterface3 extends IInterface1, IInterface2 {
...
}
// Класс с 2-я интерфейсами-предками.
public class Class1 implements IInterface2, IInterface1 {
...
}
// Класс с интерфейсом и классом в качестве предков.
public class Class2 extends Class1 implements IInterface1 {
...
}
Обратите внимание, что при наследовании интерфейса от интерфейса и класса от класса мы используем ключевое слово extends, а при наследовании класса от интерфейса мы используем ключевое слово implements.
Сериализация класса
Для того, чтобы экземпляр класса можно было сохранять (например в файл) и потом восстанавливать (например, из файла), класс должен быть сериализуемым. В этом случае сохранение / чтение экземпляра класса будет происходить не поэлементно (что тоже возможно), а целиком.
Вообще же говоря, сериализация - это представление некого объекта в последовательной форме, а десериализация - это восстановление объекта из последовательной формы.
Для того, чтобы класс стал сериализуемым, его надо просто объявить потомком интерфейса Serializable. Методов в этом интерфейсе нет.
Вот сразу пример сериализуемого класса:
import java.io.*;
public class Worker implements Serializable {
// Данные класса.
int age=0;
int yoe=0;
Worker(int age, int yoe){
this.age = age;
this.yoe = yoe;
}
// Метод main.
public static void main(String[] args)
throws IOException, ClassNotFoundException {
// Сериализация экземпляра класса.
Worker w = new Worker(22, 2);
ObjectOutputStream os = new ObjectOutputStream(
new FileOutputStream("1.txt"));
os.writeObject(w);
os.close();
// Десериализация экземпляра класса.
Worker w1 = new Worker(0, 0);
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("1.txt"));
w1 = (Worker)ois.readObject();
ois.close();
System.out.println("age = " + w1.age + ", yoe = " + w1.yoe);
}
}
Статический метод main, по идее, должен был бы быть в отдельном классе. Мы поместили его в этот же класс для упрощения. В этом методе мы сериализуем в файл 1.txt экземпляр нашего класса (со значениями полей 22 и 2) и потом десериализуем данные из этого файла в другой экземпляр этого же класса Worker. Результат будет вполне ожидаемым - значения полей в новом экземпляре класса будут такие же - 22 и 2.
Инверсия списка
Рассмотрим с вами решение классической задачи - инверсии (т. е. размещению элементов в обратном порядке) списка. Список устроен следующим образом: каждый элемент списка содержит, во-первых, собственные данные и, во-вторых, содержит ссылку на следующий элемент списка. Если следующего элемента списка нет, то в ссылке на следующий элемент содержится null. Вот тык выглядит код:
public class ListReverse
{
// Ссылка на следующий элемент списка.
public ListReverse next = null;
// Данные.
public int data = 0;
// Конструктор.
public ListReverse(ListReverse next, int data)
{
this.next = next;
this.data = data;
}
}
Как видно из кода, мы назвали наш класс ListReverse. В нем есть ссылка на следующий экземпляр класса ListReverse, у того на следующий и т. п.. У последнего в списке эта ссылка равна null.
Так как нам надо показывать для проверки содержимое нашего списка до и после инверсии, то напишем метод showAll, который будет показывать все элементы списка, задаваемого своим первым элементом. Для того, чтобы не плодить классы, мы поместим этот метод прямо в наш класс ListReverse, и сделаем его статическим. При этом список, подлежащий показу, мы передадим в качестве параметра:
static public void showAll(ListReverse list)
{
// Если список пуст.
if(list == null)
{
System.out.println("List is empty");
return;
}
// Пробегаем все элементы списка.
ListReverse curr = list;
while (curr != null){
System.out.println(curr.data);
curr = curr.next;
}
}
Этот метод мы пометим прямо в класс ListReverse.
Теперь пишем метод для инверсии. Его мы тоже сделаем статическим методом класса ListReverse, и в качестве параметра в него будет передаваться список для инверсии, а в качестве возвращаемого значения - отинверсированный список. Вот его код:
static public ListReverse reverse(ListReverse list)
{
// Если список пуст или содержит только один элемент.
if(list == null || list.next == null)
{
return list;
}
// Если в списке больше одного элемента,
// то заводим вспомогательные переменные
// и переменную для результата (res).
ListReverse aux0 = list;
ListReverse res = list.next;
ListReverse aux1 = list.next;
// Этот элемент будет в инвертированном списке последним.
aux0.next = null;
// Пробегаем весь список.
while(aux1.next != null){
aux1 = aux1.next;
res.next = aux0;
aux0 = res;
res = aux1;
};
res.next = aux0;
return res;
}
С классом ListReverse все. Теперь напишем класс для проверки. Вот его код:
public class ListReverseTest {
public static void main(String[] args) {
// Создаем список a.
// Последний элемент списка.
ListReverse a = new ListReverse(null, 0);
a = new ListReverse(a, 1);
a = new ListReverse(a, 22);
a = new ListReverse(a, -441);
// Показ элементов списка до инверсии.
ListReverse.showAll(a);
// Инверсия списка.
a = ListReverse.reverse(a);
// Показ элементов списка после инверсии.
ListReverse.showAll(a);
}
}
Результатом программы будет, естественно, -441 22 1 0 0 1 22 -441.
Класс File
Класс File предназначен для работы с файлом как с отдельным объектом. Этот класс не предназначен для работы с содержимым файла. Этот класс расположен в пространстве имен java.io.
Вот пример его использования:
import java.io.*;
public class Test {
public static void main(String[] args) throws InterruptedException, IOException {
File file = new File("C:\\tmp.txt");
// Проверяем существование файла.
exsists(file);
// Создаем новый файл.
file.createNewFile();
// Проверяем существование файла.
exsists(file);
// Переименование файла.
file.renameTo(new File("C:\\tmp2.txt"));
// Время последней модификации.
System.out.println("Last modified: " + file.lastModified());
// Имя файла.
System.out.println("Name: " + file.getName());
// Путь к файлу.
System.out.println("Parent: " + file.getParent());
// Удаление файла.
file.delete();
// Получеие разделителя.
System.out.println("Separator: " + File.separator);
// Создание временного файла.
System.out.println("Roots: " + File.createTempFile("AAA","ZZ"));
}
private static void exsists(File file) {
if(file.exists()){
System.out.println("File exists.");
}
else{
System.out.println("File doesn't exist");
}
}
}
Обратите внимание на несколько моментов. Метод renameTo предназначенный для переименования файла, должен принимать в качестве параметра (т. е. нового файла) файл, расположенный в той же папке, что и первоначальный файл (в этом случае этот метод возвращает true, в противном случае - false). Второе: при указании полного имени файла надо вместо одного слеша (\) указывать два (\\).
У класса File существует небольшое число статических методов. В нашем примере их рассматривается два - для получения разделителя в именах файлов (для Windows это "\") и для создания временного файла. Последний метод возвращает полный путь к созданному файлу (что-то типа "C:\DOCUME~1\Admin\LOCALS~1\Temp\AAA46533ZZ").
Работа с файлами и дисками
Для работы с файлами и дисками служит все тот же рассмотренный нами ранее класс File. Вот несколько примеров его использования для работы с папками и дисками:
import java.io.*;
public class Test1 {
public static void main(String[] args) {
File file = new File("C:\\Tmp2");
// Выяснянем, папка ли это.
if(file.isDirectory()){
System.out.println("Directory");
// Получаем все файлы и папки.
String [] s = file.list();
for(int i = 0; i < s.length; i++){
System.out.println(s[i]);
}
System.out.println("====");
// Получаем имена всех дисков.
File [] roots = java.io.File.listRoots();
for(int i = 0; i < roots.length; i++){
System.out.println(roots[i].getPath());
}
}
else{
System.out.print("Not a drectory");
}
// Создание папки.
File newDir = new File("C:\\qqq");
newDir.mkdir();
// Создание нескольких вложенных папок.
File newDirs = new File("C:\\qqq2\\www");
newDirs.mkdirs();
}
}
Обратите внимание, что существует два метода для создания папок - один (mkdir) позволяет создать только одну папку, другой (mkdirs) позволяет создать целую серию вложенных друг в друга папок.
Еще обратите внимание, что наряду с методом list существует метод listFiles. Отличие тут такое - первый из них позволяет получить имена вложенных файлов и папок, а второй - объекты File. Т. е. во втором случае вы можете получить не только имена вложенных папок и файлов, но и некоторую дополнительную информацию о них.
Получение системных свойств
Для получения системных свойств (таких, например, как имя пользователя, версия виртуальной java-машины или разделителя между папками для конкретной операционной системы) используется класс Properties из пространства имен java.util и метод getProperties из System. Вот пример использования:
public class Test1 {
public static void main(String[] args) {
// Получение всех свойств.
java.util.Properties properties = System.getProperties();
// Вывод отдельного свойства по имени.
String s = properties.getProperty("user.name");
System.out.print(s);
// Вывод всех свойств.
properties.list(System.out);
}
}
Обратите внимание, что мы можем получить как значение отдельного свойства (по его имени), так и сразу все свойства.
Перечисления (enum)
Перечисление - это определенный программистом тип данных, значения которого ограничиваются некоторым набором. Этот набор может содержать, например, названия дней недели. Или состояние некоторого объекта (включен, выключен). Или тип доступа к файлу (только для чтения, полный доступ или отсутствие доступа, например).
Вот пример объявления перечисления:
public enum MyColors {
Red,
Yellow,
Pink;
}
А вот так его можно использовать:
public class MainClass {
public static void main(String[] args) {
// Объявление перечисления.
MyColors color = MyColors.Yellow;
// Вывод перечисления на экран.
System.out.println(color.toString());
// Использование перечисления в switch
switch(color){
case Pink:
System.out.println("Выбран розовый");
break;
case Red:
System.out.println("Выбран красый");
break;
case Yellow:
System.out.println("Выбран желтый");
break;
}
// Получение номера элемента перечисления.
int n = color.ordinal();
System.out.println(n);
// Получение элемента перечисления по имени.
color = MyColors.valueOf("Red");
System.out.println(color.toString());
// Получение всех значений перечисления
MyColors[] values = MyColors.values();
for(int i = 0; i < values.length; i++){
System.out.println(values[i]);
}
}
}
Обратите внимание, что для инициализации переменной перечисляемого типа мы используем синтаксис типа MyColors.Yellow (имя перечисления, имя значения перечисления), а в конструкции switch мы используем только имя значения перечисления (например, Red).
Также обратите внимание на ряд полезных методов: values - позволяет получить все элементы перечисления (статический), valueOf - возвращающий элемент перечисления по строке (статический), ordinal - позволяет получить номер в перечислении определенного значения.
Перечисления (enum) 2
В Java, в отличие от многих других языков программирования, в перечисления можно добавлять методы, переменные и другие члены. Этим перечисления очень напоминают на классы.
Вот пример объявления такого перечисления:
public enum Suits {
Spades,
Clubs,
Diamonds,
Hearts;
static public boolean isRed(Suits suit){
if(suit == Suits.Diamonds || suit == Suits.Hearts){
return true;
}
else{
return false;
}
}
}
Как вы видите тут мы, кроме собственно объявления элементов перечисления (названия карточных мастей), добавили в перечисление статический метод, выясняющий, красная данная масть или нет.
А вот так можно наше перечисление использовать:
public class Test {
public static void main(String[] args){
Suits suit = Suits.Diamonds;
if(Suits.isRed(suit)){
System.out.print("Suit is red.");
}
else{
System.out.print("Suit is black.");
}
}
}
Указанный фрагмент выведен, разумеется, текст "Suit is red.".
Вызов конструктора из другого конструктора.
Часто для того, чтобы избежать написание повторяющегося кода инициализации, необходимо вызвать код одного конструктора из другого.
Вот пример, как это можно делать:
public class SomeClass {
int a;
int b;
int c;
public SomeClass(int a, int b){
this.a = a;
this.b = b;
}
public SomeClass(int a, int b, int c){
// Вызов конструктора с двумя параметрами.
this(a, b);
this.c = c;
}
public void Show(){
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
}
}
Как вы видите, для этого используется ключевое слово this, после которого в круглых скобках указываем параметры для соответствующего конструктора (с двумы параметрами в данном случае).
Игра «Пятнашки»
package prjavapytna;
import javax.swing.UIManager;
import java.awt.*;
public class ApplicationPytna
{
boolean packFrame = false;
/**Construct the application*/
public ApplicationPytna()
{
FramePytna frame = new FramePytna();
//Validate frames that have preset sizes
//Pack frames that have useful preferred
//size info, e.g. from their layout
if (packFrame)
{
frame.pack();
}
else
{
frame.validate();
}
//Center the window
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.height > screenSize.height)
{
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width)
{
frameSize.width = screenSize.width;
}
frame.setLocation((screenSize.width - frameSize.width) / 2,
(screenSize.height - frameSize.height) / 2);
frame.setVisible(true);
}
/**Main method*/
public static void main(String[] args)
{
try
{
UIManager.setLookAndFeel(
UIManager.getCrossPlatformLookAndFeelClassName());
}
catch(Exception e)
{
e.printStackTrace();
}
new ApplicationPytna();
}
}
package prjavapytna;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FramePytna extends JFrame
{
JPanel contentPane;
JPanel gamePane = new JPanel();
BorderLayout borderLayout1 = new BorderLayout();
//линия статус бара
JLabel status= new JLabel();
//массив кнопочек
JButton[][] buttons = new JButton[4][4];
//матрица для хранения чисел
int[][] matrix = new int[4][4];
/**Construct the frame*/
public FramePytna()
{
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try
{
jbInit();
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**Component initialization*/
private void jbInit() throws Exception
{
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(borderLayout1);
this.setSize(new Dimension(206,275));
this.setTitle("Пятнашки");
//запрещаем изменение размера окна
this.setResizable(false);
//Создаем основное меню
JMenuBar menuBar = new JMenuBar();
//Создаем подменю
JMenu menuGame = new JMenu("Игра");
JMenu menuHelp = new JMenu("Помощь");
//Добавляем подменю в основное меню
menuBar.add(menuGame);
menuBar.add(menuHelp);
//Устанавливаем полученное меню на окно
this.setJMenuBar(menuBar);
//Создаем элементы подменю Game с обработчиками событий
JMenuItem item1 = new JMenuItem("Новая игра");
JMenuItem item2 = new JMenuItem("Выход");
//Создаем элемент подменю Help с обработчиками событий
JMenuItem item3 = new JMenuItem("О игре...");
//Добавляем обработчики событий по нажатию
item1.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(ActionEvent e)
{
newGame();
}
});
item2.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.exit(0);//Выход из системы
}
});
item3.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(null,
"Игра Пятнашки","Об игре",
JOptionPane.QUESTION_MESSAGE);
}
});
//Добавляем созданные элементы к подменю
menuGame.add(item1);
menuGame.add(item2);
menuHelp.add(item3);
gamePane.setLayout(null);
// добавляем кнопочки
int count=0;
for(int i = 0;i<4;i++)
for(int j = 0;j<4;j++)
{
buttons[i][j] = new JButton(""+count);
//устанавливаем обработчик событий
buttons[i][j].addMouseListener(new mAdapter(i,j));
buttons[i][j].setSize(50,50);
buttons[i][j].setLocation(50*j,50*i);
buttons[i][j].setCursor(new Cursor(Cursor.HAND_CURSOR));
gamePane.add(buttons[i][j]);
matrix[i][j]=count;//задаем матрицу
count++;
}
buttons[0][0].setText(" ");
//Добавляем игровую панель с кнопками
contentPane.add(gamePane,BorderLayout.CENTER);
//обрамляем строку статуса
status.setBorder(BorderFactory.createEtchedBorder());
//Добавляем строку статуса
contentPane.add(status,BorderLayout.SOUTH);
//новая игра
newGame();
}
/**Overridden so we can exit when window is closed*/
protected void processWindowEvent(WindowEvent e)
{
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING)
{
System.exit(0);
}
}
/**Случайным образом переставляем элементы матрицы*/
public void randomizeMatrix()
{
for(int i = 0;i<100;i++)
{
int a = (int)(Math.random()*4);
int b = (int)(Math.random()*4);
int a2 = (int)(Math.random()*4);
int b2 = (int)(Math.random()*4);
int c = matrix[a][b];
matrix[a][b] = matrix[a2][b2];
matrix[a2][b2] = c;
}
}
/**Начинаем новую игру*/
public void newGame()
{
randomizeMatrix();
for(int i = 0;i<4;i++)
for(int j = 0;j<4;j++)
{
//пишем на кнопочках числа из матрицы
if(matrix[i][j]!=0)buttons[i][j].setText(""+matrix[i][j]);
else buttons[i][j].setText("");
}
status.setText("Старт новой игры");
gamePane.setVisible(true);
}
/**Класс адаптера прослушивающий события над кнопкой*/
class mAdapter extends java.awt.event.MouseAdapter
{
int posi,posj;
int startx=0;
int starty=0;
/**Конструктор адаптера в зависимости от позиции кнопочки*/
mAdapter(int posI,int posJ)
{
this.posi=posI;
this.posj=posJ;
}
/**При нажатой кнопке мыши*/
public void mousePressed(MouseEvent e)
{
//меняем курсор
buttons[posi][posj].setCursor(new Cursor(Cursor.MOVE_CURSOR));
//запоминаем точку начала переноса кнопки
startx=e.getX();
starty=e.getY();
}
/**При отпускании кнопки мыши*/
public void mouseReleased(MouseEvent e)
{
//меняем курсор
buttons[posi][posj].setCursor(new Cursor(Cursor.HAND_CURSOR));
//запоминаем точку окончания переноса кнопки
int endx=e.getX();
int endy=e.getY();
//определяем направление переноса
//если абсолютная величина (shiftx) больше
//абсолютной величины (shifty)
//значит передвигаемся по оси х
int shiftx=endx-startx;
int shifty=endy-starty;
if(Math.abs(shiftx)>Math.abs(shifty))
{
//определяем направление движения по оси х
if(shiftx>0)
{//вправо
//если это не крайние правые кнопки
//и правая кнопка пустая
//меняем местами кнопку с пустой кнопкой
if((posj != 3) && (matrix[posi][posj+1] == 0))
{
matrix[posi][posj+1] = matrix[posi][posj];
matrix[posi][posj]=0;
buttons[posi][posj].setText("");
buttons[posi][posj+1].setText(""+matrix[posi][posj+1]);
status.setText("good turn");
}else status.setText("it is impossible");
}
else
{//влево
//если это не крайние левые кнопки
//и левая кнопка пустая
//меняем местами кнопку с пустой кнопкой
if((posj != 0) && (matrix[posi][posj-1] == 0))
{
matrix[posi][posj-1] = matrix[posi][posj];
matrix[posi][posj]=0;
buttons[posi][posj].setText("");
buttons[posi][posj-1].setText(""+matrix[posi][posj-1]);
status.setText("good turn");
}else status.setText("it is impossible");
}
}
else
{
//определяем направление движения по оси y
if(shifty>0)
{//вниз
//если это не крайние нижние кнопки
//и нижняя кнопка пустая
//меняем местами кнопку с пустой кнопкой
if((posi != 3) && (matrix[posi+1][posj] == 0))
{
matrix[posi+1][posj] = matrix[posi][posj];
matrix[posi][posj]=0;
buttons[posi][posj].setText("");
buttons[posi+1][posj].setText(""+matrix[posi+1][posj]);
status.setText("Хороший ход");
}else status.setText("Проигрыш");
}
else
{//вверх
//если это не крайние верхние кнопки
//и верхняя кнопка пустая
//меняем местами кнопку с пустой кнопкой
if((posi != 0) && (matrix[posi-1][posj] == 0))
{
matrix[posi-1][posj] = matrix[posi][posj];
matrix[posi][posj]=0;
buttons[posi][posj].setText("");
buttons[posi-1][posj].setText(""+matrix[posi-1][posj]);
status.setText("good turn");
}else status.setText("it is impossible");
}
}
//проверяем все ли числа стоят на своих местах
//если да то победа!!!
int count=1;
int error=0;
for(int i = 0;i<4;i++)
for(int j = 0;j<4;j++)
{
if(matrix[i][j]!=count)error++;
count++;
}
if(error==1)
{
status.setText("Вы выиграли!!!");
int result = JOptionPane.showConfirmDialog(null,
"Вы выиграли!!!","Вы выиграли!!! Новая игра?",JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION)newGame();
else gamePane.setVisible(false);
}
}
}
}
АППЛЕТЫ