sonyps4.ru

Интерфейс в яве на пальцах. Понятие интерфейса в Java

Скажу сразу - при работе с графикой, скорее всего, со временем Вам придется воспользоваться всеми предоставленными инструментами без исключения для достижения наилучшего визуального эффекта. Подробное описание о том «что и как» можно найти - это официальный туториал по Graphics2D. Его должно быть более чем достаточно, чтобы ввести Вас в курс дела.

Я уже привел небольшой пример написания своего UI, но есть и другие варианты кастомизации интерфейса. Каждый отдельный J-компонент производит свою Lightweight-отрисовку при помощи метода paint(), который можно легко переопределить и изменить. Напрямую (не всегда, но чаще всего) его лучше не использовать (не буду вдаваться в подробности, так как это целая тема для отдельного топика). Для следующего примера используем метод paintComponent(). Рассмотрим как его можно применить поближе…

Начну с примера - текстовое поле с визуальным фидбэком при отсутствии содержимого:

JTextField field = new JTextField()
{
private boolean lostFocusOnce = false ;
private boolean incorrect = false ;

{
// Слушатели для обновления состояния проверки
addFocusListener (new FocusAdapter()
{
public void focusLost (FocusEvent e)
{
lostFocusOnce = true ;

repaint ();
}
});
addCaretListener (new CaretListener()
{
public void caretUpdate (CaretEvent e)
{
if (lostFocusOnce)
{
incorrect = getText ().trim ().equals ("" );
}
}
});
}

protected void paintComponent (Graphics g)
{
super.paintComponent (g);

// Расширенная отрисовка при некорректности данных
if (incorrect)
{
Graphics2D g2d = (Graphics2D) g;

// Включаем антиалиасинг для гладкой отрисовки
g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);

// Получаем отступы внутри поля
Insets insets;
if (getBorder () == null )
{
insets = new Insets (2, 2, 2, 2);
}
else
{
insets = getBorder ().getBorderInsets (this );
}

// Создаем фигуру в виде подчеркивания текста
GeneralPath gp = new GeneralPath (GeneralPath.WIND_EVEN_ODD);
gp.moveTo (insets.left, getHeight () - insets.bottom);
for (int i = 0; i < getWidth () - insets.right - insets.left; i += 3)
{
gp.lineTo (insets.left + i,
getHeight () - insets.bottom - ((i / 3) % 2 == 1 ? 2: 0));
}

// Отрисовываем её красным цветом
g2d.setPaint (Color.RED);
g2d.draw (gp);
}
}
};

Наличие содержимого перепроверяется при печати и потере фокуса полем. Переключившись на другой компонент мы увидим как отрисовывается наше дополнение к JTextField"у:

Полный код примера можно взять .

Таким образом можно расширить любой доступный компонент быстро переопределив и дополнив метод отрисовки, не прибегая к написанию отдельных громоздких UI или полноценных компонентов. Плюс данный пример можно достаточно легко вынести в отдельный класс и использовать как готовый элемент для своего интерфейса.
Еще одним плюсом данного способа является то, что Вы получаете независимый от текущего установленного приложению/компоненту LaF/UI - он будет работать всегда. Естественно, для некоторых специфичных UI может понадобиться немного иная отрисовка - поддерживать её или нет - Вам решать.

Хотя зачастую и хочется сделать интерфейс ярче и привлекательней, не всегда требуется для этого изобретать новые компоненты - можно воспользоваться стандартными средствами. Приведу пример кастомизации простейшего чекбокса анимированной сменой состояний и фона. В этот раз я опять (да, можете бить меня палками) воспользуюсь заготовленными изображениями из умелых рук дизайнера.

За основу берутся 8 изображений 16х16 - 4 состояния фона чекбокса и 4 состояния галки (5 на самом деле, но 5ое мы добавим програмно):

У стандартного чекбокса, конечно, нету возможности задать спрайты для анимации состояний, к тому же нам нужно наложить изображения галки на фоновые в разных вариациях. Для этого допишем отдельный метод:

public static List BG_STATES = new ArrayList ();
public static List CHECK_STATES = new ArrayList ();

static
{
// Иконки состояния фона
for (int i = 1; i <= 4; i++)
{
BG_STATES.add (new ImageIcon (
MyCheckBox.class .getResource ("icons/states/" + i + ".png" )));
}

// Дополнительное "пустое" состояние выделения

new BufferedImage (16, 16, BufferedImage.TYPE_INT_ARGB)));

// Состояния выделения
for (int i = 1; i <= 4; i++)
{
CHECK_STATES.add (new ImageIcon (
MyCheckBox.class .getResource ("icons/states/c" + i + ".png" )));
}
}

private Map iconsCache = new HashMap ();

private synchronized void updateIcon ()
{
// Обновляем иконку чекбокса
final String key = bgIcon + "," + checkIcon;
if (iconsCache.containsKey (key))
{
// Необходимая иконка уже была ранее использована
setIcon (iconsCache.get (key));
}
else
{
// Создаем новую иконку совмещающую в себе фон и состояние поверх
BufferedImage b = new BufferedImage (BG_STATES.get (0).getIconWidth (),
BG_STATES.get (0).getIconHeight (), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = b.createGraphics ();
g2d.drawImage (BG_STATES.get (bgIcon).getImage (), 0, 0,
BG_STATES.get (bgIcon).getImageObserver ());
g2d.drawImage (CHECK_STATES.get (checkIcon).getImage (), 0, 0,
CHECK_STATES.get (checkIcon).getImageObserver ());
g2d.dispose ();

ImageIcon icon = new ImageIcon (b);
iconsCache.put (key, icon);
setIcon (icon);
}
}

Остается добавить несколько обработчиков переходов состояний и мы получим анимированный переход между ними:

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

Таким образом путей кастомизации элементов есть достаточно много (даже я, наверно, не знаю/не догадываюсь о некоторых из них). Какой способ выбирать - зависит от ситуации - что необходимо получить, какие изменения возможно сделать в существующем компоненте и т.д.

В завершение данной главы, приведу еще один пример полностью видоизмененного UI кнопки с анимацией, возможностью загругления отдельных углов, возможностью настройки стиля и некоторыми другими улучшениями. Вот несколько скринов с итоговым внешним видом (конечно, анимация тут не видна):


Не буду врать, на данный UI кнопки ушло достаточно много времени, но он создавался без помощи и подсказок дизайнера чистыми средствами Graphics2D и Swing. можно скачать и изучить полный сурс и демо данного UI, если Вам стало интересно. В нем использован достаточно большой спектр возможностей Graphics2D и применены некоторые хитрости, которые могут зачастую оказаться полезными.

Итак, думаю достаточно разговоров о графике - о ней более подробно я расскажу в будущих топиках, а сейчас приведу немного интересного материала, который я наработал за достаточно долгое время «общения» со Swing и Graphics2D.

DnD и GlassPane

Думаю первое - Вам всем более чем известно, как и проблемы с ним связанные. Насчет второго - вероятно Вы вскользь слышали о GlassPane или может даже видели это старинное изображение (которое до сих пор актуально, между прочем) об устройстве различных слоев стандартных фреймов. Что же тут такого и зачем я вспомнил об этом? И тем более, как связаны DnD и GlassPane спросите Вы? Вот именно о том, как их связать и что из этого может выйти я и хочу рассказать в этой главе.

Чтож, начнем по порядку - что мы знаем о DnD?
У некоторых Swing-компонентов есть готовые реализации для драга (JTree и JList к примеру) - для других можно достаточно легко дописать свою. Чтобы не бросаться словами на ветер - приведу небольшой пример DnD стринга из лэйбла:

JLabel label = new JLabel ("Небольшой текст для DnD" );
label.setTransferHandler (new TransferHandler()
{
public int getSourceActions (JComponent c)
{
return TransferHandler.COPY;
}

public boolean canImport (TransferSupport support)
{
return false ;
}

protected Transferable createTransferable (JComponent c)
{
return new StringSelection (((JLabel) c).getText ());
}
});
label.addMouseListener (new MouseAdapter()
{
public void mousePressed (MouseEvent e)
{
if (SwingUtilities.isLeftMouseButton (e))
{
JComponent c = (JComponent) e.getSource ();
TransferHandler handler = c.getTransferHandler ();
handler.exportAsDrag (c, e, TransferHandler.COPY);
}
}
});

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

Но что делать, если необходимо отследить последовательность действий пользователя при самом перетаскивании?
Для этого есть отдельная возможность повесить слушатель:

DragSourceAdapter dsa = new DragSourceAdapter()
{
public void dragEnter (DragSourceDragEvent dsde)
{
// При входе драга в область какого-либо компонента
}

public void dragExit (DragSourceEvent dse)
{
// При выходе драга в область какого-либо компонента
}

public void dropActionChanged (DragSourceDragEvent dsde)
{
// При смене действия драга
}

public void dragOver (DragSourceDragEvent dsde)
{
// При возможности корректного завершения драга
}

public void dragMouseMoved (DragSourceDragEvent dsde)
{
// При передвижении драга
}

public void dragDropEnd (DragSourceDropEvent dsde)
{
// При завершении или отмене драга
}
};
DragSource.getDefaultDragSource ().addDragSourceListener (dsa);
DragSource.getDefaultDragSource ().addDragSourceMotionListener (dsa);

Остался последний момент - обозначить роль GlassPane. GlassPane, фактически, позволяет располагать/отрисовывать на себе компоненты, как и любой другой контейнер, но его особенность в том, что он лежит поверх всех Swing-компонентов, когда виден. Т.е. если мы что-либо отрисуем на нем, то оно накроет весь находящийся под ним интерфейс. Это позволяет размещать компоненты независимо от основного контейнера в любом месте, создавать любые визуальные эффекты и делать другие занятные вещи.

Приведу для большего понимания небольшой пример подобного «эффекта» - фрейм с несколькими Swing-компонентами на нем. При клике в любой части окна будет появляться эффект «распозающегося» круга, который виден поверх всех элементов. Что самое интересное - подобный эффект не съедает ресурсов и не требует большой груды кода. Не верите? - посмотрите демо и загляните в исходник, вложенный в jar.

Кстати говоря, есть достаточно интересная библиотека на эту тему, заодно предоставляющая дополнительный скролл-функционал и несколько других вкусностей - JXLayer (офф сайт) (описание #1 описание #2 описание #3). К сожалению проекты хостящиеся на сайте java сейчас находтся не в лучшем состоянии, поэтому приходится ссылаться на отдельные ресурсы.

Итак теперь объединим всё что я уже описал в данной главе и сделаем, наконец, что-то полноценное. К примеру - отображение драга панели с компонентами внутри окна:

При драге панели за лэйбл появляется полупрозрачная копия панели показывающая где именно будет размещена панель при завершении драга. Также клавишей ESC можно отменить перемещение.
Рабочий пример и исходный код можно взять .

Конечно, для реализации именно данной функциональности не стоило бы прибегать к использованию DnD - есть более короткие пути. Впрочем, всё зависит от ситуации. Данный вариант позволяет отрисовывать перетаскивание независимо от других компонентов в окне. Также можно на основе него, к примеру, реализовать драг панели между окнами приложения.

AWTUtilities

Так как уже достаточно давно в JDK6 включили некоторые будущие нововведения из 7ки, не могу обойти их стороной, так как с их помощью возможно много чего сделать приложив при этом гораздо меньшие усилия.
Итак, нас интересует несколько методов из AWTUtilities:
  1. AWTUtilities.setWindowShape(Window, Shape) - позволяет установить любому окну определенную форму (будь то круг или хитрый полигон). Для корректной установки формы окно не должно быть декорировано нативным стилем (setUndecorated(true)).
  2. AWTUtilities.setWindowOpacity (Window, float) – позволяет задать прозрачность окна от 0 (полностью прозрачно) до 1 (непрозрачно). Окно при этом может быть декорировано нативным стилем.
  3. AWTUtilities.setWindowOpaque (Window, boolean) – позволяет полностью спрятать отображение фона и оформления окна, но при этом любой размещенный на нем компонент будет виден. Для корректной установки данного параметра окно также как и в п.1 не должно быть декорировано нативным стилем.

Что же нам это дает? На самом деле - достаточно широкий спектр возможностей. Окнам своего приложения можно задавать любые хитрые формы какие Вам только потребуются, делать «дырки» посреди приложения, создавать кастомные тени под окно, делать приятные на вид попапы и пр. и пр.

Если переходить к конкретике - setWindowShape на деле я никогда не использую, так как задаваемая окну форма строго обрезается по краю и не очень приятно выглядит. На помощь приходит setWindowOpaque - спрятав оформление и фон окна можно с помощью контейнера с кастомным отрисованным фоном создавать абсолютно любые окна. Приведу небольшой пример использования (в нем также есть также использованы некоторые приемы из предыдущих глав поста):

можно взять работающий jar с исходным кодом. Честно скажу, что потратил на данный пример не более десяти минут (из них - минут пять прикидывал как расположить элементы внутри диалога:). Естественно это лишь один из вариантов примемения этих новых возможностей - на самом деле их куда больше.

Единственная неприятная мелочь в использовании AWTUtilities – нестабильная работа на Linux-системах. Т.е. Не везде и не всегда корректно отрабатывает прозрачность окон. Не уверен, проблемы ли это текущей JDK или же ОС.

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

Я уже поверхностно рассказал о том, как создавать компоненты, UI и некоторые «навороты» для интерфейса своего приложения, но что же делать, если нам нужно добавить функциональную часть к компоненту или создать свой совершенно новый компонент с некоей функциональностью и стилизацией? Стилизовать стандартные компоненты и делать из них отдельные части нового компонента достаточно долгое и нудное занятие, тем более что при малейшем изменении в одном из компонентов вся схема может поехать. Именно в таких случаях стоит сделать свой компонент «с нуля».

Итак, за основу лучше лучше всего взять JComponent и используя paint-методы отрисовать его содержимое. Фактически JСomponent сам по себе - чистый холст с некоторыми зашитыми улучшениями для отрисовками и готовыми стандартными методами setEnabled/setFont/setForeground/setBackground и т.п. Как использовать (и использовать ли их) решать Вам. Все, что Вы будете добавлять в методы отрисовки станет частью компонента и будет отображаться при добавлении его в какой-либо контейнер.

Кстати, небольшое отступление, раз уж зашла речь о контейнерах, - любой наследник и сам JComponent являются контейнерами, т.е. могут содержать в себе другие компоненты, которые будет расположены в зависимости от установленного компоненту лэйаута. Что же творится с отрисовкой чайлд-компонентов, лежащих в данном и как она связана с отрисовкой данного компонента? Ранее я не вдавался подробно в то, как устроены и связаны paint-методы Jcomponent"а, теперь же подробно опишу это…

Фактически, paint() метод содержит в себе вызовы 3ех отдельных методов - paintComponent, paintBorder и paintChildren. Конечно же дополнительно он обрабатывает некоторые «особенные» случаи отрисовки как, например печать или перерисовку отдельной области. Эти три метода всегда вызываются в показанной на изображении выше последовательности. Таким образом сперва идет отрисовка самого компонента, затем поверх рисуется бордер и далее вызывается отрисовка чайлд-компонентов, которые в свою очередь также вызывают свой paint() метод и т.д. Естественно есть еще и различные оптимизации, предотвращающие лишние отрисовки, но об этом подробнее я напишу потом.

Компонент отрисован, но статичен и представляет собой лишь изображение. Нам необходимо обработать возможность управления им мышью и различными хоткеями.
Для этого, во-первых, необходимо добавить соответствующие слушатели (MouseListener/MouseMotionListener/KeyListener) к самому компоненту и обрабатывать отдельные действия.

Чтобы не объяснять все на пальцах, приведу пример компонента, позволяющего визуально ресайзить переданный ему ImageIcon:

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

При создании данного компонента я бы выделил несколько важных моментов:

  1. Определяемся с функционалом и внешним видом компонента - в данном случае это область с размещенным на ней изображением, бордером вокруг изображения и 4мя ресайзерами по углам. Каждый из ресайзеров позволяет менять размер изображения. Также есть возможность передвигать изображение по области, «схватив» его за центр.
  2. Определяем все необходимые для работы компонента параметры - в данном случае это само изображение и его «опорные» точки (верхний левый и правый нижний углы). Также есть ряд переменных, которые понадобятся при реализации ресайза и драга изображения.
  3. Накидываем заготовку для компонента (желательно отдельный класс, если Вы собираетесь его использовать более раза) - в данном случае создаем класс ImageResizeComponent, определяем все необходимые для отрисовки параметры, переопределяем метод paintComponent() и отрисовываем содержимое. Также переопределяем метод getPreferredSize(), чтобы компонент сам мог определить свой «желаемый» размер.
  4. Реализовываем функциональную часть компонента - в данном случае нам будет достаточно своего MouseAdapter"а для реализации ресайза и передвижения. При нажатии мыши в области проверяем координаты и сверяем имх с координатами углов и самого изображения - если нажатие произошло в районе некого угла - запоминаем его и при драге изменяем его координату, ежели нажатие пришлось на изображение - запоминаем начальные его координаты и при перетаскивании меняем их. И наконец, последний штрих - в mouseMoved() меняем курсор в зависимости от контрола под мышью.
Ничего сложного, правда? С реализацией «кнопочных» частей других компонентов всё еще проще - достаточно проверять, что нажатие пришлось в область кнопки. Параллельно с отслеживанием событий можно также визуально менять отображение компонента (как сделано в данном примере на ресайзерах). В общем сделать можно всё, на что хватит фантазии.

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

Важно помнить

Есть несколько вещей, которые стоит при любой работе с итерфейсными элементами в Swing – приведу их в этой отдельной небольшой главе.
  1. Любые операции вызова перерисовки/обновления компонентов должны производится внутри Event Dispatch потока для избежания визуальных «подвисаний» интерфейса Вашего приложения. Выполнить любой код в Event Dispatch потоке достаточно легко:
    SwingUtilities.invokeLater (new Runnable()
    {
    public void run ()
    {
    // Здесь располагаете исполняемый код
    }
    });
    Важно также помнить, что события, вызываемые из различных listener"ов стандартных компонентов (ActionListener/MouseListener и пр.) исходно вызываются из этого потока и не требуют дополнительной обработки.
  2. Из любых paint-методов ни в коем случае нельзя влиять на интерфейс, так как это может привести к зацикливаниям или дедлокам в приложении.
  3. Представьте ситуацию - из метода paint Вы изменяете состояние кнопки, скажем, через setEnabled(enabled). Вызов этого метода заставляет компонент перерисоваться и заново вызвать метод paint и мы опять попадаем на этот злополучный метод. Отрисовка кнопки зациклиться и интерфейс приложения будет попросту висеть в ожидании завершения отрисовки (или как минимум съест добрую часть процессора). Это самый простой вариант подобной ошибки.
  4. Не стоит производить тяжёлые вычисления внутри Event Dispatch потока. Вы можете произвести эти вычисления в отдельном обычном потоке и затем только вызвать обновление через SwingUtilities.invokeLater().
  5. Не стоит, также, производить тяжёлые вычисления внутри методов отрисовки. По возможности стоит выносить их отдельно и кэшировать/запоминать. Это позволит ускорить отрисовку компонентов, улучшить общую отзывчиввость приложения и снизить нагрузку на Event Dispatch поток.
  6. Для обвноления внешнего вида компонентов используйте метод repaint() (или же repaint(Rectangle) – если Вам известна точная область для обновления), сам метод repaint необходимо исполнять в Event Dispatch потоке. Для обновления же расположения элементов в лэйауте используйте метод revalidate() (его нет необходимости исполнять в Event Dispatch потоке). Метод updateUI() может помочь в некоторых случаях для полного обновления элемента (например смене данных таблицы), но его нужно использовать аккуратно, так как он также отменит установленный Вами UI и возьмет UI предоставляемый текущим LaF"ом.
  7. Полная установка LaF всему приложению отменит любые ваши вручную заданные в ходе работы UI компонентов и установит поверх них те UI, которые предлагает устанавливаемый LaF. Поэтому лучше производить установку LaF при загрузке приложения до открытия каких-либо окон и показа визуальных элементов.
Следовние этим простым пунктам позволит Вам не беспокоиться о возникновении непредвиденных «тормозов» или дедлоков в интерфейсе приложения.
Думаю, что этот список можно дополнить еще несколькими пунктами, но они уже будут весьма специфичны/необязательны.

Итоги

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

Еще хотелось бы добавить, что возможно, название статьи может показаться Вам слишком громким и не полностью раскрытым - действительно, я все же не дизайнер и не юзабилити-специалист. Я лишь предлагаю Вам инструменты/способы, которые позволят расширить и улучшить интерфейс приложения. Непосредственно бОльшая (не обязательно вся) часть работы по созданию графики для слайдера/кнопки или определению общего стиля компонентов и самого приложения всегда остается за дизайнером - от этого никуда не уйти.

Все примеры статьи в едином «флаконе». Из начального окна можно выбрать желаемый пример:

  • interface
  • dnd
  • customization
  • интерфейс
  • кастомизация
  • Добавить метки

    Пакет (package ) - это некий контейнер, ко­торый используется для того, чтобы изолиро­вать имена классов. Например, вы можете со­здать класс List, заключить его в пакет и не думать после этого о возможных конфликтах, которые могли бы возникнуть если бы кто-ни­будь еще создал класс с именем List.

    Интерфейс - это явно указанная спецификация набора методов, ко­торые должны быть представлены в классе, который реализует эту спе­цификацию. Реализация же этих методов в интерфейсе отсутствует. По­добно абстрактным классам интерфейсы обладают замечательным дополнительным свойством - их можно многократно наследовать. Кон­кретный класс может быть наследником лишь одного суперкласса, но зато в нем может быть реализовано неограниченное число интерфейсов.

    Пакеты

    Все идентификаторы, которые мы до сих пор использовали в наших примерах, располагались в одном и том же пространстве имен (name space). Это означает, что нам во избежание конфликтных ситуаций при­ходилось заботиться о том, чтобы у каждого класса было свое уникаль­ное имя. П акеты - это механизм, который служит как для работы с пространством имен, так и для ограничения видимости. У каждого файла.java есть 4 одинаковых внутренних части, из которых мы до сих пор в наших примерах ис­пользовали только одну. Ниже приведена общая форма исходного файла Java.

    одиночный оператор package (необязателен)

    любое количество операторов import (необязательны)

    одиночное объявление открытого (public) класса

    любое количество закрытых (private) классов пакета (необязательны)

    Оператор package

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

    package java.awt.image;

    то и исходный код этого класса должен храниться в каталогеjava/awt/image.

    ЗАМЕЧАНИЕ

    Каталог, который транслятор Java будет рассматривать, как корневой для иерархии пакетов, можно задавать с помощью переменной окру­жения СLASSPATH. С помощью этой переменной можно задать не­сколько корневых каталогов для иерархии пакетов (через ; как в обычном PATH).

    Трансляция классов в пакетах

    При попытке поместить класс в пакет, вы сразу натолкнетесь на жесткое требование точ­ного совпадения иерархии каталогов с иерархией пакетов. Вы не можете переименовать пакет, не переименовав каталог, в котором хранятся его классы. Эта трудность видна сразу, но есть и менее очевидная проблема.

    Представьте себе, что вы написали класс с именем PackTest в пакете test. Вы создаете каталог test, помещаете в этот каталог файл PackTest.Java и транслируете. Пока - все в порядке. Однако при попытке запустить его вы получаете от интерпретатора сообщение «can"t find class PackTest» («He могу найти класс PackTest»). Ваш новый класс теперь хранится в пакете с именем test, так что т еперь надо указывать всю иерархию пакетов, разделяя их имена точками - test.PackTest. Кроме того Вам надо либо под­няться на уровень выше в иерархии каталогов и снова набрать «java test.PackTest», либо внести в переменную CLASSPATH каталог, который является вершиной иерархии разрабатываемых вами классов.

    Оператор import

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

    import пакет1 [.пакет2].(имякласса|*);

    Здесь пакет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. В трех последу­ющих главах вы освоите работу с

    An interface is just like Java Class, but it only has static constants and abstract method. Java uses Interface to implement multiple inheritance. A Java class can implement multiple Java Interfaces. .

    Syntax for Declaring Interface

    Interface { //methods }

    To use an interface in your class, append the keyword "implements" after your class name followed by the interface name.

    Example for Implementing Interface

    Class Dog implements Pet interface RidableAnimal extends Animal, Vehicle

    Why is an Interface required?

    To understand the concept of Java Interface better, let see an example. The class "Media Player" has two subclasses: CD player and DVD player. Each having its unique implementation method to play music.

    Another class "Combo drive" is inheriting both CD and DVD (see image below). Which play method should it inherit? This may cause serious design issues. And hence, Java does not allow multiple inheritance.

    Now let"s take another example of Dog.

    Suppose you have a requirement where class "dog" inheriting class "animal" and "Pet" (see image below). But you cannot extend two classes in Java. So what would you do? The solution is Interface.

    The rulebook for interface says,

    • An interface is 100% abstract class and has only abstract methods.
    • Class can implement any number of interfaces.

    Class Dog can extend to class "Animal" and implement interface as "Pet".

    Java Interface Example:

    Step 1) Copy following code into an editor.

    Interface Pet{ public void test(); } class Dog implements Pet{ public void test(){ System.out.println("Interface Method Implemented"); } public static void main(String args){ Pet p = new Dog(); p.test(); } }

    Step 2) Save , Compile & Run the code. Observe the Output.

    Difference between Class and Interface

    Class Interface
    In class, you can instantiate variable and create an object. In an interface, you can"t instantiate variable and create an object.
    Class can contain concrete(with implementation) methods The interface cannot contain concrete(with implementation) methods

    The access specifiers used with classes are private, protected and public.

    In Interface only one specifier is used- Public.

    When to use Interface and Abstract Class?

    • Use an abstract class when a template needs to be defined for a group of subclasses
    • Use an interface when a role needs to be defined for other classes, regardless of the inheritance tree of these classes

    Must know facts about Interface

    • A Java class can implement multiple Java Interfaces. It is necessary that the class must implement all the methods declared in the interfaces.
    • Class should override all the abstract methods declared in the interface
    • The interface allows sending a message to an object without concerning which classes it belongs.
    • Class needs to provide functionality for the methods declared in the interface.
    • All methods in an interface are implicitly public and abstract
    • An interface can extend from one or many interfaces. Class can extend only one class but implement any number of interfaces
    • An interface cannot implement another Interface. It has to extend another interface if needed.
    • An interface which is declared inside another interface is referred as nested interface
    • At the time of declaration, interface variable must be initialized. Otherwise, the compiler will throw an error.
    • The class cannot implement two interfaces in java that have methods with same name but different return type.

    Summary :

    • The class which implements the interface needs to provide functionality for the methods declared in the interface
    • All methods in an interface are implicitly public and abstract
    • An interface cannot be instantiated
    • An interface reference can point to objects of its implementing classes
    • An interface can extend from one or many interfaces. A class can extend only one class but implement any number of interfaces

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


    Чтобы размещать кнопки, текстовые надписи и другие компоненты в окне программы, вам следует понять, как работает 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 ...?" ) ; } }

    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 , константы доступны любому классу, независимо от пакета.

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

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

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

    }

    Загрузка...