Интерфейсы Java созданы для поддержки динамического выбора (resolution) методов во время выполнения программы. Интерфейсы похожи на классы, но в отличие от последних у интерфейсов нет переменных представителей, а в объявлениях методов отсутствует реализация. Класс может иметь любое количество интерфейсов. Все, что нужно сделать - это реализовать в классе полный набор методов всех интерфейсов. Сигнатуры таких методов класса должны точно совпадать с сигнатурами методов реализуемого в этом классе интерфейса. Интерфейсы обладают своей собственной иерархией, не пересекающейся с классовой иерархией наследования. Это дает возможность реализовать один и тот же интерфейс в различных классах, никак не связанных по линии иерархии классового наследования. Именно в этом и проявляется главная сила интерфейсов.

8.2.1. Оператор interface

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

interface имя {
тип_результата имя_метода1 (список параметров);
тип имя_ finall _переменной = значение;
}

Обратите внимание - у объявляемых в интерфейсе методов отсутствуют операторы тела. Объявление методов завершается символом «;» (точка с запятой). В интерфейсе можно объявлять и переменные, при этом они неявно объявляются переменными типа final. Это означает, что класс реализации не может изменять их значения. Кроме того, при объявлении переменных в интерфейсе их обязательно нужно инициализировать константными значениями. Ниже приведен пример определения интерфейса, содержащего единственный метод с именем callback и одним параметром типа int.

interface Callback {
void callback(int param);
}

8.2.2. Оператор implements

Оператор implements - это дополнение к определению класса, реализующего некоторый интерфейс.
class имякласса [ extends суперкласс]
[ implements интерфейс0 [, интерфейс 1...]]
{тело класса}

Если в классе реализуется несколько интерфейсов, то их имена разделяются запятыми. Ниже приведен пример класса, в котором реализуется определенный нами интерфейс:

class Client implements Callback {
void callback(int p) {
System.out.println("callback вызван с " + p);
}
}

В очередном примере метод callback интерфейса, определенного ранее, вызывается через переменную-ссылку на интерфейс:

class Testlface {
public static void main(String args)
{

switch(result) {
case NO:
System.out.println("He т ");
break;
case YES:
System.out.println(" Д a");
break;
case MAYBE:
System.out.println("Mo ж e т быть ");
break;
case LATER:
System.out.println(" Позже ");
break;
case SOON:
System.out.priniln("C к opo");
break;
case NEVER:
System.out.println(" Никогда ");
break;
}
}

public static void main(String args) {
Question q = new Question();
answer(q.ask());
answer(q.ask());
answer(q.ask());
answer (q . ask ());
}
}

Обратите внимание на то, что результаты при разных запусках программы отличаются, поскольку в ней используется класс генерации случайных чисел Random пакета java.util.

Позже
Скоро
Нет
Да

Привет! Сегодня поговорим о важном понятии в Java - интерфейсы. Слово тебе наверняка знакомо. Например, интерфейсы есть у большинства компьютерных программ и игр. В широком смысле интерфейс - некий «пульт», который связывает две взаимодействующие друг с другом стороны. Простой пример интерфейса из повседневной жизни - пульт от телевизора. Он связывает два объекта, человека и телевизор, и выполняет разные задачи: прибавить или убавить звук, переключить каналы, включить или выключить телевизор. Одной стороне (человеку) нужно обратиться к интерфейсу (нажать на кнопку пульта), чтобы вторая сторона выполнила действие. Например, чтобы телевизор переключил канал на следующий. При этом пользователю не обязательно знать устройство телевизора и то, как внутри него реализован процесс смены канала. Все, к чему пользователь имеет доступ - это интерфейс . Главная задача - получить нужный результат. Какое это имеет отношение к программированию и Java? Прямое:) Создание интерфейса очень похоже на создание обычного класса, только вместо слова class мы указываем слово interface . Давай посмотрим на простейший Java-интерфейс, и разберемся, как он работает и для чего нужен: public interface Swimmable { public void swim () ; } Мы создали интерфейс Swimmable - «умеющий плавать ». Это что-то вроде нашего пульта, у которого есть одна «кнопка»: метод swim() - «плыть». Как же нам этот «пульт » использовать? Для этого метод, т.е. кнопку нашего пульта, нужно имплементировать . Чтобы использовать интерфейс, его методы должны реализовать какие-то классы нашей программы. Давай придумаем класс, объекты которого подойдут под описание «умеющий плавать». Например, подойдет класс утки - Duck: public class Duck implements Swimmable { public void swim () { System. out. println ("Уточка, плыви!" ) ; } public static void main (String args) { Duck duck = new Duck () ; duck. swim () ; } } Что же мы здесь видим? Класс Duck «связывается» с интерфейсом Swimmable при помощи ключевого слова implements . Если помнишь, мы использовали похожий механизм для связи двух классов в наследовании, только там было слово «extends ». « public class Duck implements Swimmable » можно для понятности перевести дословно: «публичный класс Duck реализует интерфейс Swimmable ». Это значит, что класс, связанный с каким-то интерфейсом, должен реализовать все его методы. Обрати внимание: в нашем классе Duck прямо как в интерфейсе Swimmable есть метод swim() , и внутри него содержится какая-то логика. Это обязательное требование. Если бы мы просто написали « public class Duck implements Swimmable » и не создали бы метод swim() в классе Duck , компилятор выдал бы нам ошибку: Duck is not abstract and does not override abstract method swim() in Swimmable Почему так происходит? Если объяснять ошибку на примере с телевизором, получится, что мы даем человеку в руки пульт с кнопкой «переключить канал» от телевизора, который не умеет переключать каналы. Тут уж нажимай на кнопку сколько влезет, ничего не заработает. Пульт сам по себе не переключает каналы: он только дает сигнал телевизору, внутри которого реализован сложный процесс смены канала. Так и с нашей уткой: она должна уметь плавать, чтобы к ней можно было обратиться с помощью интерфейса Swimmable . Если она этого не умеет, интерфейс Swimmable не свяжет две стороны - человека и программу. Человек не сможет использовать метод swim() , чтобы заставить объект Duck внутри программы плыть. Теперь ты увидел более наглядно, для чего нужны интерфейсы. Интерфейс описывает поведение , которым должны обладать классы, реализующие этот интерфейс. «Поведение» - это совокупность методов. Если мы хотим создать несколько мессенджеров, проще всего сделать это, создав интерфейс Messenger . Что должен уметь любой мессенджер? В упрощенном виде, принимать и отправлять сообщения. public interface Messenger { public void sendMessage () ; public void getMessage () ; } И теперь мы можем просто создавать наши классы-мессенджеры, имплементируя этот интерфейс. Компилятор сам «заставит» нас реализовать их внутри классов. Telegram: public class Telegram implements Messenger { public void sendMessage () { System. out. println ("Отправляем сообщение в Telegram!" "Читаем сообщение в Telegram!" ) ; } } WhatsApp: public class WhatsApp implements Messenger { public void sendMessage () { System. out. println ("Отправляем сообщение в WhatsApp!" ) ; } public void getMessage () { System. out. println ("Читаем сообщение в WhatsApp!" ) ; } } Viber: public class Viber implements Messenger { public void sendMessage () { System. out. println ("Отправляем сообщение в Viber!" ) ; } public void getMessage () { System. out. println ("Читаем сообщение в Viber!" ) ; } } Какие преимущества это дает? Самое главное из них - слабая связанность . Представь, что мы проектируем программу, в которой у нас будут собраны данные клиентов. В классе Client обязательно нужно поле, указывающее, каким именно мессенджером клиент пользуется. Без интерфейсов это выглядело бы странно: public class Client { private WhatsApp whatsApp; private Telegram telegram; private Viber viber; } Мы создали три поля, но у клиента запросто может быть всего один мессенджер. Просто мы не знаем какой. И чтобы не остаться без связи с клиентом, приходится «заталкивать» в класс все возможные варианты. Получается, один или два из них всегда будут null , и они вообще не нужны для работы программы. Вместо этого лучше использовать наш интерфейс: public class Client { private Messenger messenger; } Это и есть пример «слабой связанности»! Вместо того, чтобы указывать конкретный класс мессенджера в классе Client , мы просто упоминаем, что у клиента есть мессенджер. Какой именно - определится в ходе работы программы. Но зачем нам для этого именно интерфейсы? Зачем их вообще добавили в язык? Вопрос хороший и правильный! Того же результата можно добиться с помощью обычного наследования, так ведь? Класс Messenger - родительский, а Viber , Telegram и WhatsApp - наследники. Действительно, можно и так. Но есть одна загвоздка. Как ты уже знаешь, множественного наследования в Java нет . А вот множественная реализация интерфейсов - есть. Класс может реализовывать сколько угодно интерфейсов . Представь, что у нас есть класс Smartphone , у которого есть поле Application - установленное на смартфоне приложение. public class Smartphone { private Application application; } Приложение и мессенджер, конечно, похожи, но все-таки это разные вещи. Мессенджер может быть и мобильным, и десктопным, в то время как Application - это именно мобильное приложение. Так вот, если бы мы использовали наследование, не смогли бы добавить объект Telegram в класс Smartphone . Ведь класс Telegram не может наследоваться одновременно от Application и от Messenger ! А мы уже успели унаследовать его от Messenger , и в таком виде добавить в класс Client . Но вот реализовать оба интерфейса класс Telegram запросто может! Поэтому в классе Client мы сможем внедрить объект Telegram как Messenger , а в класс Smartphone - как Application . Вот как это делается: public class Telegram implements Application , Messenger { //...методы } public class Client { private Messenger messenger; public Client () { this . messenger = new Telegram () ; } } public class Smartphone { private Application application; public Smartphone () { this . application = new Telegram () ; } } Теперь мы используем класс Telegram как захотим. Где-то он будет выступать в роли Application , где-то - в роли Messenger . Наверняка ты уже обратил внимание, что методы в интерфейсах всегда «пустые», то есть они не имеют реализации. Причина этого проста: интерфейс описывает поведение, а не реализует его. «Все объекты классов, имплементирующих интерфейс Swimmable , должны уметь плавать»: вот и все, что говорит нам интерфейс. Как там конкретно будет плавать рыба, утка или лошадь - вопрос к классам Fish , Duck и Horse , а не к интерфейсу. Также как переключение канала - задача телевизора. Пульт просто предоставляет тебе кнопку для этого. Впрочем, в Java8 появилось интересное дополнение - методы по умолчанию (default method). Например, в твоем интерфейсе есть 10 методов. 9 из них реализованы по-разному в разных классах, но один реализован одинаково у всех. Раньше, до выхода Java8, методы внутри интерфейсов вообще не имели реализации: компилятор сразу выдавал ошибку. Теперь же можно сделать вот так: public interface Swimmable { public default void swim () { System. out. println ("Плыви!" ) ; } public void eat () ; public void run () ; } Используя ключевое слово default , мы создали в интерфейсе метод с реализацией по умолчанию. Два других метода, eat() и run() , нам необходимо будет реализовать самим во всех классах, которые будут имплементировать Swimmable . С методом swim() этого делать не нужно: реализация будет во всех классах одинаковой. Кстати, ты уже не раз сталкивался с интерфейсами в прошлых задачах, хоть и не замечал этого сам:) Вот очевидный пример: Ты работал с интерфейсами List и Set ! Точнее, с их реализациями - ArrayList , LinkedList , HashSet и прочими. На этой же схеме видно пример, когда один класс реализует сразу несколько интерфейсов. Например, LinkedList реализует интерфейсы List и Deque (двусторонняя очередь). Ты знаком и с интерфейсом Map , а точнее, с его реализаций - HashMap . Кстати, на этой схеме ты можешь увидеть одну особенность: интерфейсы могут быть унаследованы друг от друга. Интерфейс SortedMap унаследован от Map , а Deque наследуется от очереди Queue . Это нужно, если ты хочешь показать связь интерфейсов между собой, но при этом один интерфейс является расширенной версией другого. Давай рассмотрим пример с интерфейсом Queue - очередь. Мы пока не проходили коллекции Queue , но они достаточно простые и устроены как обычная очередь в магазине. Добавлять элементы можно только в конец очереди, а забирать - только из начала. На определенном этапе разработчикам понадобился расширенный вариант очереди, чтобы добавлять и получать элементы можно было с обеих сторон. Так создали интерфейс Deque - двустороннюю очередь. В нем присутствуют все методы обычной очереди, ведь она является «родителем» двусторонней, но при этом добавлены новые методы.

public void paint(Graphics g)

// Рисование изображения типа Image

g.drawlmage(picture, 35, 35, this ); // this -ссылка апплета на себя

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

Обращение к полю класса в этом случае: this.имя_поля_класса

 super - используется как ссылка на экземпляр суперклассас целью обеспечения доступа к нестатическимполям и методам суперкласса.

Доступ кполю суперкласса обеспечивается выражением:super.имя_поля_класса

class ClassA { float x;

class ClassB extends ClassA { int x;

public void method l(int x) {

int iX1 = x; // присваивание значения параметра метода int iX2 = this.x; // присваивание значения поля данного класса float fX = super.x; // присваивание значения поля суперкласса

Доступ к методу суперкласса обеспечивается выражением:super.имя_метода_класса()

Возможность обращения к методу суперкласса полезна при переопределении метода. Если необходимо, в теле переопределенного метода в классе-потомке можно организовать вызов кода старого метода из суперкласса. Например:

public void method2() {...} ...

class ClassB extends ClassA {

public void method2() { super.method2();

Использование this и super в конструкторах - см. пример ConstrDemo.java с комментариями.

Интерфейсы в Java

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

Интерфейс - это подобие абстрактного класса, особый вид класса без реализации. Интерфейс

реализуется классом.

Объекты одного класса используют общее поведение (методы). Интерфейс позволяетдополнить описание поведения объекта новыми функциональными возможностями, объявляя набор методов, которые должен поддерживать класс,

реализующий данный интерфейс. Реализация методов осуществляется в классе, а не в интерфейсе.

Скачано с сайта http://ivc.clan.su

Интерфейс представляет собой набор методов без реализации (без тел), который впоследствии может бытьреализован классом.В интерфейсе указывается, что некоторый класс должен делать, но не

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

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

Класс в Java может реализовывать несколько интерфейсов, и один интерфейс -использоваться несколькими классами.

Таким образом, интерфейсы можно использовать для придания определенных функциональных возможностей (иными словами, способностей - "ability " - выполнять определенные функции) самым разнообразным классам:как классам,

которые исходно совершенно не связаны между собой, так и классам, связанным между собой иерархией наследования. В Java есть немало интерфейсов, названии которых имеется характерный суффикс " able " (например, Runnable).

Определение интерфейса , как и определение класса в Java, содержит два компонента:

объявление и тело.

Объявление интерфейса имеет вид:

Interface Имя_интерфейса *extends Список_суперинтерфейсов+

1. Модификаторы интерфейса

Public - спецификатордоступа - указывает, что к данному интерфейсу возможен доступ из других пакетов. В случаеотсутствия спецификатора public , доступ разрешен только внутри пакета, в котором интерфейс находится.

Подобно классам public, интерфейс public должен описываться в файле с именем Имя_интерфейса.jаvа.

Подразумевается, что каждый интерфейс имеет модификатор реализацииabstract ,который не указывается при объявлении .

2. Имя интерфейса

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

3. Спецификация суперинтерфейсов

Суперинтерфейсы указываются ключевым словом extends .

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

Любой класс, реализующий производный интерфейс, должен определить тела всех методов, объявленных как в данном производном интерфейсе, так и в суперинтерфейсе(ах).

Тело интерфейса заключено в,- и может включать только объявленияметодов и объявления констант (static final полей).

Объявления констант

Каждая переменная (поле) интерфейса по умолчанию считается переменной с модификаторамиpublic ,static иfinal . Эти модификаторы не обязательно указывать при объявлении, однако на практике полезно указывать их явно в качестве постоянного напоминания о статусе поля

(открытая статическая константа ) себе и другим программистам.

Инициализация констант выполняется при объявлении.

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

Обращение к константе: Имя_интерфейса.Имя_константы

Объявления методов

Объявление метода, после которого ставится знак ";" , имеет вид:

.(имякласса|*);

Здесь пакет1 - имя пакета верхнего уровня, пакет2 - это необя­зательное имя пакета, вложенного в первый пакет и отделенное точкой. И, на­конец, после указания пути в иерархии пакетов, указывается либо имя класса, либо метасимвол звездочка. Звездочка означает, что, если Java-транслятору потребуется какой-либо класс, для которого пакет не указан явно, он должен просмотреть все содержимое пакета со звездочкой вмес­то имени класса. В приведенном ниже фрагменте кода показаны обе формы использования оператора import:

import java.util.Date

import java.io.*;

ЗАМЕЧАНИЕ

Но использовать без нужды ф орму записи оператора import с использованием звездочки не рекомендуется, т.к. это может значительно увеличить время трансляции кода (на скорость работы и размер программы это не влияет).

Все встроенные в Java классы, которые входят в комплект поставки, хранятся в пакете с именем java. Базовые функции языка хранятся во вложенном пакете java.lang. В есь этот пакетавтоматически импортируется транслятором во все программы. Это эквивалентно размещению в начале каждой программы оператора

import java.lang.*;

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

class MyDate extends Java.util.Date { }

Ограничение доступа

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

Подклассы в том же пакете.

Не подклассы в том же пакете.

Подклассы в различных пакетах.

Классы, которые не являются подклассами и не входят в тот же пакет.

В языке Java имеется три уровня доступа, определяемых ключевыми словами: private (закрытый), public (открытый) и protected (защищен­ный), которые употребляются в различных комбинациях. Содержимое ячеек таблицы определяет доступность переменной с данной комбинацией модификаторов (столбец) из указанного места (строка).

модификатор отсутствует

private protected

тот же класс

подкласс в том же пакете

независимый класс в том же пакете

подкласс в дру­гом пакете

независимый класс в другом пакете

На первый взгляд все это может показаться чрезмерно сложным, но есть несколько правил, которые помогут вам разобраться. Элемент, объ­явленный public, доступен из любого места. Все, что объявлено private, доступно только внутри класса, и нигде больше. Если у элемента вообще не указан модификатор уровня доступа, то такой элемент будет виден из подклассов и классов того же пакета. Именно такой уровень доступа используется в языке Java по умолчанию. Если же вы хотите, чтобы элемент был доступен извне пакета, но только подклассам того класса, которому он принадлежит, вам нужно объявить такой элемент protected. И наконец, если вы хотите, чтобы элемент был доступен только под­классам, причем независимо от того, находятся ли они в данном пакете или нет - используйте комбинацию private protected.

Ниже приведен довольно длинный пример, в котором представлены все допустимые комбинации модификаторов уровня доступа. В исходном коде первого пакета определяется три класса: Protection, Derived и SamePackage. В первом из этих классов определено пять целых переменных - по одной на каждую из возможных комбинаций уровня доступа. Переменной n приписан уровень доступа по умолчанию, n_pri - уровень private, n_pro - protected, n_pripro - private protected и n_pub - public. Во всех остальных классах мы пытаемся использовать переменные первого класса. Те строки кода, которые из-за ограничения доступа при­вели бы к ошибкам при трансляции, закомментированы с помощью однострочных комментариев (//) - перед каждой указано, откуда доступ при такой комбинации модификаторов был бы возможен. Второй класс - Derived - является подклассом класса Protection и расположен в том же пакете р1. Поэтому ему доступны все перечислен­ные переменные за исключением n_pri. Третий класс, SamePackage, расположен в том же пакете, но при этом не является подклассом Protection. По этой причине для него недоступна не только переменная n_pri, но и n_pripro, уровень доступа которой - private protected.

package р1;

public class Protection {

int n = 1;

private int n_pri = 2;

protected int n_pro = 3;

private protected int n_pripro = 4;

public int n_pub = 5;

public Protection() {

System.out.println("base constructor");

System.out.println("n = " + n);

System.out.println("n_pri = " + n_pri);

System.out.println("n_pro = " + n_pro);

System.out.println("n_pripro = " + n_pripro);

System.out.println("n_pub = " + n_pub);

} }

class Derived extends Protection {

Derived() {

System.out.println("derived constructor");

System.out.println("n = " + n);

// только в классе

// System.out.println("n_pri = " + n_pri);

System.out.println("n_pro = "+ n_pro);

System.out.println("n_pripro = " + n_pripro);

System.out.println("n_pub = "+ n_pub);

} }

classSamePackage {

SamePackage() {

Protection p = new Protection();

System.out.println("same package constructor");

System.out.println("n = " + p.n);

// только в классе

// System.out.println("n_pri = " + p.n_pri);

System.out.println("n_pro = " + p.n_pro);

// только в классе и подклассе

// System.out.println("n_pripro = " + p.n_pripro):

System.out.println("n_pub = " + p.n_pub):

} }

Интерфейсы

Интерфейсы Java созданы для поддержки динамического выбора (resolution) методов во время выполнения программы. Интерфейсы похожи на классы, но в отличие от последних у интер­фейсов нет переменных представителей, а в объявлениях методов отсут­ствует реализация. Класс может иметь любое количество интерфейсов. Все, что нужно сделать - это реализовать в классе полный набор методов всех интерфейсов. Сигнатуры таких методов класса должны точно совпадать с сигнатурами методов реализуемого в этом классе интерфейса. Интер­фейсы обладают своей собственной иерархией, не пересекающейся с классовой иерархией наследования. Это дает возможность реализовать один и тот же интерфейс в различных классах, никак не связанных по линии иерархии классового наследования. Именно в этом и проявляется главная сила интерфейсов. Интерфейсы являются аналогом механизма множественного наследования в C++, но использовать их намного легче.

Оператор interface

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

interface имя {

тип_результата имя_метода1(список параметров);

тип имя_final1-переменной = значение;

}

Обратите внимание - у объявляемых в интерфейсе ме­тодов отсутствуют операторы тела. Объявление методов завершается сим­волом; (точка с запятой). В интерфейсе можно объявлять и переменные, при этом они не­явно объявляются final - переменными. Это означает, что класс реализа­ции не может изменять их значения. Кроме того, при объявлении переменных в интерфейсе их обязательно нужно инициализировать кон­стантными значениями. Ниже приведен пример определения интерфейса, содержащего единственный метод с именем callback и одним параметром типа int.

interface Callback {

void callback(int param);

}

Оператор implements

Оператор implements - это дополнение к определению класса, реали­зующего некоторый интерфейс(ы).

class имя_класса

] { тело класса }

Если в классе реализуется несколько интерфейсов, то их имена раз­деляются запятыми. Ниже приведен пример класса, в котором реализуется определенный нами интерфейс:

class Client implements Callback {

void callback(int p) {

System.out.println("callback called with " + p);

} }

В очередном примере метод callback интерфейса, определенного ранее, вызывается через переменную - ссылку на интерфейс:

class TestIface {

public static void main(String args) { Callback с = new client();

c.callback(42);

} }

Ниже приведен результат работы программы:

С:\> Java TestIface

callback called with 42

Переменные в интерфейсах

Интерфейсы можно использовать для импорта в различные классы со­вместно используемых констант. В том случае, когда вы реализуете в классе какой-либо интерфейс, все имена переменных этого интерфейса будут видимы в классе как константы. Это аналогично использованию файлов-заголовков для задания в С и C++ констант с помощью директив #define или ключевого слова const в Pascal / Delphi.

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

import java.util.Random;

interface SharedConstants { int NO = 0;

int YES = 1;

int MAYBE = 2;

int LATER = 3;

int SOON = 4;

int NEVER = 5; }

class Question implements SharedConstants {

Random rand = new Random();

int ask() {

int prob = (int) (100 * rand.nextDouble());

if (prob < 30)

return NO; // 30% else if (prob < 60)

return YES; // 30% else if (prob < 75)

return LATER; // 15% else if (prob < 98)

return SOON; // 13% else

return NEVER; // 2% } }

class AskMe implements SharedConstants {

static void answer(int result) {

switch(result) {

case NO:

System.out.println("No");

break;

case YES:

System.out.println("Yes");

break;

case MAYBE:

System.out.println("Maybe");

break;

case LATER:

System.out.println("Later");

break;

case SOON:

System.out.priniln("Soon");

break;

case NEVER:

System.out.println("Never");

break;

} }

public static void main(String args) {

Question q = new Question();

answer(q.ask());

answer(q.ask());

answer(q.askO);

answer(q.ask());

} }

Обратите внимание на то, что результаты при разных запусках програм­мы отличаются, поскольку в ней используется класс генерации случай­ных чисел Random пакета java.util. Описание этого пакета приведено в главе 12 .

С:\> Java AskMe

Later

Scon

No

Yes

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

Теперь вы обладаете полной информацией для создания собственных пакетов классов. Легко понимае­мые интерфейсы позволят другим программистам использовать ваш код для самых различных целей. Инструменты, которые вы приобрели, изу­чив эту и предыдущую главы, должны вам помочь при разработке любых объектно-ориентированных приложений. В дальнейшем вы позна­комитесь с некоторыми важными специфическими свойствами Java, ко­торые представлены в виде классов в пакете java.lang. В трех последу­ющих главах вы освоите работу с

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


Чтобы размещать кнопки, текстовые надписи и другие компоненты в окне программы, вам следует понять, как работает JPanel . Это что-то вроде контейнера для компонентов, который занимает прямоугольную область экрана и показывает компоненты, выровненные по какому-то простому принципу. Как именно выровнены компоненты, зависит от типа схемы размещения, которую вы установили для панели. Для простых задач программирования вам следует знать, как минимум, BorderLayout , который располагает компоненты по краям и один большой компонент ставит в середину, затем FlowLayout , который обычно располагает компоненты в ряд по горизонтали, и, наконец, GridLayout , который располагает компоненты в произвольной таблице n * m. Есть другие типы, но они слишком сложны для новичков. Ключевая идея в том, что "компонентой" может быть не только кнопка или флажок, но и другая JPanel. Вы можете получить достаточно сложный пользовательский интерфейс, просто расположив панели одна на другой и выбрав для них планировку.


Если у вас есть экземпляр объекта JPanel, вызовите метод .setLayout , чтобы установить тип планировки, и затем метод.add, чтобы добавить на панель компоненты. В случае BorderLayout в качестве второго параметра вам нужно будет передать положение. Например, чтобы расположить кнопку в верхней части, вызовите myPanel.add(myButton, BorderLayout.North) .


Контейнер самого высокого уровня, который появляется на экране, представляющем приложение Java, является экземпляром JFrame , а не JPanel . Чтобы добавить вашу основную панель в экземпляр JFrame просто вызовите myJFrame.getContentPane().add(myJPanel, BorderLayout.Center) .


Чтобы заставить ваше приложение делать что-то большее, чем просто появляться, вам нужно будет понять интерфейс ActionListener . У любого неабстрактного ActionListener есть только один метод actionPerformed, который вызывается, когда пользователь выполняет "действие" над компонентой, в которой зарегистрирован слушатель (например, действие над кнопкой - это, очевидно, ее нажатие). Чтобы зарегистрировать слушателя действий для кнопки или любого другого компонента, вызовите метод .addActionListener. .

Шаги

Создание общего фрейма

    Создайте класс, который расширяет класс JFrame . Этот класс будет содержать все ваши компоненты графического интерфейса (GUI), такие как кнопки и текстовые поля.

    Продумайте общий внешний вид вашего первого приложения. Неплохо было бы начать с центральной панели типа BorderLayout с другой панелью в нижней ее части (BorderLayout.South ). Эта вторая панель будет иметь тип FlowLayout и содержать несколько кнопок, флажков и других контрольных элементов. И наконец, расположите большой компонент JTextArea посередине центрального компонента. Чтобы взаимодействовать с пользователем посредством текста, вы сможете использовать методы getText() и setText() .

    Напишите конструктор для вашего класса. Этот конструктор должен создать все панели и компоненты, которые вы запланировали, расположить их правильно и добавить последнюю панель, которая "прикрепляет все" к вашему фрейму (myFrame.getContentPane().add(myLargePanel, BorderLayout.Center).

    Напишите метод main, который будет точкой входа программы. В этом методе создайте экземпляр фрейма, установите его начальные размер и положение (используйте.setSize(x,y) и .setLocation(width, height) ), и заставьте его появиться на экране, вызвав .setVisible(true).

    Программирование ответов на действия пользователя

    1. Сделайте ваш фрейм реализующим интерфейс ActionListener . Это позволит вашему классу "слушать" действия компонентов.

      Для каждой кнопки, флажка или другого контрольного элемента, которых вы создали, вызовите метод. addActionListener, передав ваш фрейм (this ) в качестве параметра.

      Переопределите абстрактный метод класса ActionListener, который называется actionPerformed(ActionEvent event). В этом методе вам следует добавить условные выражения "if", чтобы проверить, откуда пришло событие. В этом условном операторе "if" должно быть условие вроде такого: "if (event.getSource() == button1 )". Здесь проверяется, откуда пришло событие, и пришло ли оно от кнопки. Внутри выражения "if" выполняйте любые действия, которые вам необходимы при нажатии на кнопку.

      У JTextArea есть метод .setText("myText") , который является неплохим способом запрограммировать какой-то видимый ответ на ваше действие.

    • Совсем не намного сложнее реализовать интерфейс MouseListener и использовать .addMouseListener , чтобы зарегистрировать его для любой компоненты.
    • Если вам нужно запросить от пользователя ввести какую-то строку, вызовите статический метод JOptionPane.showInputDialog(this, "My message"). первым параметром должен быть фрейм вашего приложения или какая-нибудь панель (поле для ввода появится посередине родительского элемента). Метод возвращает значение, которое пользователь ввел в диалоговом окне.
    • Вполне возможно разместить все компоненты на одной панели, использующей тип GridBagLayout, но этим классом труднее управлять.
    • Если вы хотите нарисовать собственные графические объекты (например, шахматную доску), почитайте о компоненте Canvas . Он может быть размещен в вашем приложении, как любой другой компонент, но вам нужно будет написать метод.paint, который полностью отвечает за его отрисовку.
    • Во многих реальных приложениях наиболее полезным компонентом Swing является JTable . После изучения основ, продолжайте работать с ним.

    Предупреждения

    • Некоторые средства разработки предлагают возможность составить графический интерфейс Swing способом, "удобным для пользователя". Однако, зачастую они не могут должным образом сделать панель с продвинутыми возможностями. Эти возможности включают деревья, таблицы, списки и комбинированные списки, которые меняют свое содержимое по мере работы программы, а также компоненты с моделями данных пользователя и т.д. Код, написанный при таком "дружеском для пользователя" способе, станет настоящим кошмаром, если вам потом потребуется дописать его вручную. Поэтому слишком не увлекайтесь подобными "дизайнерами графического интерфейса, дружественными для пользователя", потому что это ограничит ваши возможности из-за их ограниченных возможностей.
    • Swing - это однопоточное приложение. Если обработка действия у вас занимает слишком много времени, оно "зависнет", пока не произойдет выход из метода .actionPerformed . Изучайте и используйте многопоточность java, чтобы Swing оставался "живым", пока работает какой-то трудоемкий процесс.
    • Большинство методов компонентов Swing можно безопасно вызвать только из потока диспетчеризации событий (метод .actionPerformed и другие похожие методы слушателя). Если вам нужно вызвать их из какого-то другого потока (например, чтобы обновить индикатор прогресса или показать результаты какого-то длительного процесса), почитайте о SwingUtils.invokeLater .

    Источники

    Исходный код

    Import java.awt.BorderLayout ; import java.awt.FlowLayout ; import java.awt.event.ActionEvent ; import java.awt.event.ActionListener ; import javax.swing.JButton ; import javax.swing.JCheckBox ; import javax.swing.JFrame ; import javax.swing.JPanel ; import javax.swing.JTextArea ; /** * Очень простое приложение java swing. * Содержит кнопку и флажок. Отвечает * на изменения этих контрольных элементов * изменением текста в главном текстовом поле. * * @author audriusa */ public class WikiHow extends JFrame implements ActionListener { /** * Кнопка. */ JButton myButton = new JButton ("Button" ) ; /** * Флажок. */ JCheckBox myCheckBox = new JCheckBox ("Check" ) ; /** * Текстовое поле. */ JTextArea myText = new JTextArea ("My text" ) ; /** * Нижняя панель, содержащая кнопку. */ JPanel bottomPanel = new JPanel () ; /** * Родительская панель, содержащая все. */ JPanel holdAll = new JPanel () ; /** * Конструктор. */ public WikiHow() { bottomPanel.setLayout (new FlowLayout () ) ; bottomPanel.add (myCheckBox) ; bottomPanel.add (myButton) ; holdAll.setLayout (new BorderLayout () ) ; holdAll.add (bottomPanel, BorderLayout .SOUTH ) ; holdAll.add (myText, BorderLayout .CENTER ) ; getContentPane() .add (holdAll, BorderLayout .CENTER ) ; myButton.addActionListener (this ) ; myCheckBox.addActionListener (this ) ; setDefaultCloseOperation(DISPOSE_ON_CLOSE) ; } /** * Программа * @param args Параметры старта программы, не используются. */ public static void main(String args) { WikiHow myApplication = new WikiHow() ; // Указываем, где оно должно появиться: myApplication.setLocation (10 , 10 ) ; myApplication.setSize (300 , 300 ) ; // Показать! myApplication.setVisible (true ) ; } /** * Любой неабстрактный класс, который реализует ActionListener * должен иметь этот метод. * * @param e Событие. */ public void actionPerformed(ActionEvent e) { if (e.getSource () == myButton) myText.setText ("A button click" ) ; else if (e.getSource () == myCheckBox) myText.setText ("The checkbox state changed to " + myCheckBox.isSelected () ) ; else myText.setText ("E ...?" ) ; } }