ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ JAVA

А. А. ЕВДОКИМОВ, Н. В. МАЙСТРЕНКО

ВВЕДЕНИЕ

Язык программирования высокого уровня Java относится к объектно-ориентированным языкам. На него оказали влияние такие языки программирования как C++ и Smaltalk. Популярность язык Java получил благодаря идеям, которые реализовали его разработчики и заключающимся в возможностях кроссплатформенного исполнения программ, встроенных механизмов для работы в компьютерных сетях и создания многопоточных приложений, а также полной поддержки всех концепций объектно-ориентированной парадигмы программирования.

Реализация кроссплатформенности заключается в формировании из исходного кода программы специального байт-кода, который может выполняться только виртуальной машиной Java (Java Virtual Machine, JVM), установленной на конкретной платформе. Таким образом, приложения Java, представленные файлами на байт-коде (*.class), могут исполняться на любом оборудовании и любой операционной системой, в которой есть среда исполнения JVM. Виртуальная машина Java в данной схеме служит в качестве интерпретатора. В современных JVM реализована JIT-компиляция, которая увеличивает производительность приложений за счет динамической компиляции часто исполняемых фрагментов байт-кода в машинный код.

Поддержка сетевых технологий также способствовало развитию Java. Первые версии языка давали возможность писать апплеты – клиентские приложения, которые выполнялись в интернет-браузерах. Для серверов язык Java используется напрямую через CGI (Common Gateway Interface), в виде сервлетов – приложений Java, выполняющихся в отдельной среде (контейнере сервлетов), и сценариев JSP (Java Server Pages). Java поддерживает также работу с большинством современных СУБД через унифицированный доступ с помощью стандартов JDBC (Java Database Connectivity) и SQLJ.

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

В настоящее время Java активно используется для разработки мобильных приложений, выполняющихся в операционной системе Android, что продолжает способствовать популярности языка. Android-приложения на Java выполняются виртуальными машинами Dalvik и ART. Несмотря на особенности разработки мобильных приложений (работа на автономных системах, ресурсозависимость и т.д.), программисты могут использовать все возможности языка Java.



ЛАБОРАТОРНАЯ РАБОТА №1

Создание приложения Java на основе класса JFrame

Цель работы: Освоить основные элементы проектирования оконных приложений Java. Изучить особенности создания и запуска оконных приложений. Ознакомиться с архитектурой класса JFrame.

Методические указания к выполнению лабораторной работы

Разработка приложений с графическим пользовательским интерфейсом (Graphical User Interface, GUI) в Java построена на использовании библиотеки классов Swing (javax.swing), являющейся альтернативой и основанной на базовой подсистеме GUI Abstract Window Toolkit (AWT).

Классы Swing были разработаны с целью снять некоторые ограничения AWT, связанные с зависимостью внешнего вида (а в некоторых случаях и поведения) визуальных компонентов от платформы, на которой запускается приложение. Базовый набор элементов управления, окон и диалогов AWT использовал на каждой платформе нативный («родной») набор ресурсов. Это приводило к разному внешнему виду одного и того же приложения на разных платформах, фиксированному внешнему виду каждого конкретного компонента, невозможности управлять дополнительными характеристиками отображения, такими как прозрачность и форма. Такое поведение AWT не поддерживало основную идею разработчиков Java, заключенную в тезисе «write once, run everywhere». Компоненты AWT также называют тяжеловесными компонентами, так как они используют «родные» ресурсы кода платформы. За некоторыми исключениями, компоненты Swing являются облегченными (легковесными), так как написаны целиком на Java. Внешний вид каждого компонента определяется классами Swing, а не базовой операционной системой. Таким образом, каждый компонент будет выглядеть и функционировать одинаково на всех платформах.

Кроме того Swing поддерживает принцип подключаемого внешнего вида, когда тот отделен от поведения компонента. Преимуществом данного подхода является легкость изменения способа визуализации каждого компонента без затрагивания логики его работы. Такой подход также позволяет заранее определить целые наборы различных способов визуализации, которые задают разные стили GUI. Преимуществами подключаемого внешнего вида является возможность создать специальные стили для конкретных платформ, стили, одинаковые для всех платформ, а также динамически изменять стиль компонентов приложения во время его работы.

В основе графического пользовательского интерфейса с использованием классов Swing находятся два набора ключевых элементов: компоненты и контейнеры. Компонент представляет собой отдельный визуальный элемент управления, например, текстовое поле или кнопка. Контейнер в свою очередь является особым типом компонента и предназначен для объединения и содержания других компонентов. Более того, для отображения компонента необходимым условием является его нахождения внутри конкретного контейнера. В любом приложении Java, построенном на классах Swing, есть как минимум один контейнер. Так как контейнеры также являются и компонентами, то любой контейнер может содержать другие контейнеры. И для построения сложных приложений определяется иерархия вместимости, в которой выделяются контейнеры верхнего уровня.

В Swing определены два типа контейнеров: контейнеры верхнего уровня и облегченные контейнеры.

К контейнерам верхнего уровня относятся JFrame, JApplet, JWindow и JDialog. Эти классы контейнеров не являются производными от класса JComponent. Базовыми для них классами являются AWT-классы Component и Container, поэтому контейнеры верхнего уровня являются тяжеловесными компонентами (единственные в библиотеке Swing). Контейнеры верхнего уровня также называют окнами, которые являются основой пользовательского интерфейса любой операционной системы. Окна визуально разделяют выполняемые в среде приложения. Так как контейнеры верхнего уровня являются тяжеловесными, их внешний вид будет зависеть от конкретной операционной системы. Контейнер верхнего уровня не может находиться ни в каком другом контейнере. Каждая иерархия вместимости для любого приложения с GUI должна начинаться с контейнера верхнего уровня.

Родителем всех окон Swing является окно без рамки и без элементов управления JWindow. Класс JWindow определяет базовые возможности оконных приложений, например, закрытие или перемещение. Окно JFrame наследует свойства класса JWindow и является самым часто используемым контейнером верхнего уровня. У JFrame в отличие от JWindow есть рамка, которая позволяет изменять размер окна, заголовок с названием приложения (может быть пустым), кнопки управления для закрытия и свертывания окна. В окне JFrame также есть возможность использовать системное меню, позволяющее выполнять стандартные действия над окном и приложением.

Например, простое оконное приложение Java на основе класса JFrame с компонентом JButton:

import javax.swing.*;

public class SimpleJFrame extends JFrame {
	
	private javax.swing.JButton jButton1;

	public SimpleJFrame() {
		initComponents();
	}

	private void initComponents() {
		jButton1 = new JButton();
		setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		jButton1.setText("This is Button");
		GroupLayout layout = new GroupLayout(getContentPane());
		getContentPane().setLayout(layout);
		layout.setHorizontalGroup( 
			layout.createParallelGroup(GroupLayout.Alignment.LEADING)
			.addGroup(layout.createSequentialGroup()
			.addGap(68, 68, 68)
			.addComponent(jButton1)
			.addContainerGap(58, Short.MAX_VALUE))
		);
		layout.setVerticalGroup(
			layout.createParallelGroup(GroupLayout.Alignment.LEADING)
			.addGroup(layout.createSequentialGroup()
			.addGap(45, 45, 45)
			.addComponent(jButton1)
			.addContainerGap(46, Short.MAX_VALUE))
		);
		pack();
	}       
                
	public static void main(String args[]) {
		java.awt.EventQueue.invokeLater(new Runnable() {
			public void run() {
				new SimpleJFrame().setVisible(true);
			}
		});
	}
}

Вторым типом контейнеров в Swing являются облегченные контейнеры. Они являются производными от JComponent. Примером легковесного контейнера является JPanel. Облегченные контейнеры часто используют для объединения компонентов в группы по структурному или функциональному признаку, упрощая управление таких компонентов.

Каждый контейнер верхнего уровня включает в себя набор панелей (pane). Панель JRootPane представляет собой легковесный контейнер, который находится вверху иерархии вместимости панелей и предназначен для управления остальными панелями. JRootPane может управлять также линейкой меню приложения. Панели, которые содержатся в корневой панели, называются «стеклянной» панелью (glass pane), панелью содержимого (content pane) и многослойной панелью (layered pane).

Многослойная панель является экземпляром JLayeredPane. Многослойная панель предназначена для управления глубиной расположения компонентов. Значение, соответствующее этой глубине, определяет, какой компонент перекрывает собой другой компонент. Многослойная панель содержит панель содержимого и линейку меню приложения. Панель содержимого предназначена для размещения визуальных компонентов приложения и по умолчанию является непрозрачным экземпляром JPanel. Любой компонент, который добавляется в контейнер верхнего уровня, попадает в панель содержимого. «Стеклянная» панель находится над всеми остальными панелями и по умолчанию является прозрачным экземпляром JPanel. Она может перехватывать и управлять событиями мыши или рисовать поверх любого другого компонента.

Каким образом в панели содержимого будут располагаться компоненты, определяет менеджер расположения или компоновщик (layout manager). Независимо от операционной системы, версии виртуальной машины Java, разрешения и размеров экрана менеджер расположения гарантирует, что компоненты будут иметь предпочтительный или близкий к нему размер и располагаться в том порядке, который был указан разработчиком при создании программы.

Поддержка компоновщиков определена в базовом классе контейнеров java.awt.Container. Все Swing-компоненты являются производными классами от класса JComponent, для которого базовым является класс Container. Таким образом, для любого компонента Swing можно определить, какой менеджер размещения используется им в данный момент или установить нужный менеджер. Для этого используются методы getLayout() и setLayout().

Основными менеджерами расположения являются BorderLayout, FlowLayout, GridLayout, BoxLayout, CardLayout и GroupLayout.

BorderLayout – менеджер для полярного расположения. Предназначен для обычных и диалоговых окон. Данный компоновщик позволяет просто и быстро разместить наиболее часто используемые элементы любого окна: панель инструментов, строку состояния и основное содержимое. Для этого BorderLayout разбивает окно на четыре области, а все оставшееся место заполняется компонентом, выполняющим основную функцию приложения. Чтобы добавить с помощью менеджера расположения BorderLayout компонент, в его методе add() необходимо использовать дополнительный параметр, который определяет область контейнера для размещения компонента. Значениями этого параметра могут быть:

  • BorderLayout.NORTH – в этом случае компонент располагается вдоль верхней границы окна и растягивается на всю его ширину. Обычно в этом месте размещается панель инструментов;
  • BorderLayout.SOUTH – компонент располагается вдоль нижней границы и растягивается на всю ширину окна. Такое положение характерно для строки состояния;
  • BorderLayout.WEST – компонент располагается вдоль левой границы окна и растягивается на всю его высоту. При этом учитываются размеры северных и южных компонентов, имеющих приоритет;
  • BorderLayout.EAST – компонент располагается вдоль правой границы окна;
  • BorderLayout.CENTER – компонент помещается в центр окна, занимая максимально возможное пространство.

FlowLayout – менеджер для последовательного расположения. Он размещает компоненты в контейнере слева направо, сверху вниз. При полном заполнении компонентами строки контейнера FlowLayout переходит на следующую строку вниз. Данное расположение устанавливается по умолчанию в панелях JPanel. Основным свойством FlowLayout является определение предпочтительного размера компонентов.

GridLayout – менеджер для табличного расположения. Он представляет контейнер в виде таблицы с ячейками одинакового размера. Количество строк и столбцов можно указать в конструкторе. Имеется возможность задать произвольное количество либо строк, либо столбцов, но не одновременно. Все ячейки таблицы имеют одинаковый размер, равный размеру самого большого компонента, находящегося в таблице.

Менеджер расположения BoxLayout позволяет управлять размещением компонентов либо в вертикальном, либо в горизонтальном направлениях и управлять пространством между компонентами, используя вставки. Для размещения компонентов в вертикальной плоскости необходимо конструктору передать константу BoxLayout.Y_AXIS, для размещения в горизонтальной – BoxLayout.X_AXIS.

Менеджер расположения CardLayout можно использовать для создания вкладок (tabs), выбирая которые будут избранно открываться разные панели, занимающие одно и то же место в интерфейсе окна. В библиотеке Swing данную задачу можно решить с использованием класса JTabbedPane, организовывающего управление панелями с вкладками.

Менеджер расположения компонентов GroupLayout раскладывает компоненты по группам. Группы имеют горизонтальное и вертикальное направление и могут быть параллельными и последовательными. В последовательной группе у каждого следующего компонента координата вдоль оси на единицу больше (имеется в виду координата в сетке), в параллельной – компоненты имеют одну и ту же координату.

Контрольные вопросы

 1. Какие проблемы ограничений базовой библиотеки AWT были решены в библиотеке Swing?

 2. В чем заключается принцип подключаемого внешнего вида?

 3. Для чего предназначены контейнеры библиотеки Swing?

 4. Перечислите контейнеры верхнего уровня библиотеки Swing.

 5. Что инкапсулирует класс JFrame?

 6. Для чего используются легковесные контейнеры библиотеки Swing?

 7. Перечислите базовый набор панелей приложения, указывая их назначение.

 8. Для чего используется менеджеры расположения?

 9. Как управляет расположением компонентов BorderLayout?

10. Как управляет расположением компонентов FlowLayout?


Видео для подготовки

Исходные коды проекта из видео:



ЛАБОРАТОРНАЯ РАБОТА №2

Решение простых вычислительных задач на Java. Компоненты пользовательского интерфейса Swing. Формирование и компоновка GUI. Делегирование обработки событий

Цель работы: Изучить основные компоненты набора классов Swing. Исследовать возможности среды разработки по формированию и компоновке графического интерфейса пользователя. Изучить принцип делегирования обработки событий, наборы обработчиков для компонентов пользовательского интерфейса.

Методические указания к выполнению лабораторной работы

В общем случае компоненты из библиотеки Swing происходят от класса JComponent, который предлагает функциональные возможности, общие для всех компонентов. Например, JComponent поддерживает подключаемый внешний вид. JComponent наследует AWT-классы Container и Component, следовательно, основан на компоненте AWT и совместим с ним. Все компоненты Swing представлены классами, определенными в пакете javax.swing и начинаются с буквы «J».

Основными классами элементов управления Swing являются JLabel, JButton, JToggleButton, JCheckBox, JRadioButton, JComboBox, JList, JTextField, JPasswordField, JFormattedTextField, JTextArea, JScrollBar, JSlider, JProgressBar, JSpinner, JSeparator, JEditorPane, JTextPane и JTree.

JLabel – используется для вывода статического текста (метки). Возможен вывод текста с изображением (иконкой) и применение html-разметки для форматированного вывода.

JButton – используется для взаимодействия с пользователем, представляет собой кнопку (прямоугольник с текстом и/или изображением). Обычно для кнопки регистрируется слушатель события действия (нажатие кнопки).

JToggleButton – представляет собой кнопку, которая может находиться и в обычном состоянии, и в нажатом. Состояние кнопки при этом фиксируется. Когда пользователь взаимодействует с такой кнопкой, она изменяет свое состояние на противоположное. Управление внешним видом компонента осуществляется так же, как и у JButton.

JCheckBox – представляет собой элемент для выбора (флажок). Класс JCheckBox является производным от JToggleButton, отличается от последнего только внешним видом: квадрат, в который можно поставить или убрать галочку. Функциональность наследуется от JToggleButton. JCheckBox обычно используется в меню настроек для множественного выбора из группы элементов.

JRadioButton – представляет собой элемент для выбора (переключатель). Класс JRadioButton также является наследником класса JToggleButton. Внешний вид элемента: круг, в который можно поставить или убрать точку. Однако в группе элементов JRadioButton он ведет себя как переключатель. Если пользователь выбирает конкретный элемент («устанавливает точку»), то с ранее выбранного элемента выбор снимается («убирается точка»). JRadioButton также используется в меню настроек, но для выбора одного элемента из группы элементов.

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

JList – используется для отображения группы элементов (списка). В отличие от компонента JComboBox отображает одновременно сразу несколько элементов списка (весь список или его часть), кроме того пользователь может выбрать в списке также несколько элементов (если установлен соответствующий режим выделения).

JTextField – используется для ввода и редактирования однострочного текста. Для обработки текстовых данных, введенных пользователем, обычно используют слушатель события действия, который получает событие после нажатия пользователем клавиши Enter.

JPasswordField – представляет собой однострочный текстовый редактор. Работа с компонентом производится аналогично, как и с JTextField. Единственным отличием данного компонента от JTextField является то, что весь введенный в него текст заменяется специальными символами (звездочками или другими заданными символами). Обычно используется для ввода паролей.

JFormattedTextField – используется для форматированного ввода данных и представляет собой однострочный текстовый редактор. Используя средства форматирования данного компонента, можно установить текстовые поля для ввода дат, индексов, телефонов, адресов сайтов, электронной почты и т.д., то есть данных, для которых есть общепринятый формат. Такой компонент может облегчить работу разработчика по валидации полученных от пользователя исходных данных.

JTextArea – представляет собой многострочный текстовый редактор. Класс JTextArea является производным от JTextField и наследует все его методы, дополняя их методами для управления вводом нескольких строк. При вводе пользователь разделяет строки обычным способом, нажимая клавишу Enter на клавиатуре. Если текст формируется динамически в программе, то для разделения строк используется специальный управляющий символ «\n». JTextArea обычно помещается в панель (контейнер) с полосами прокрутки JScrollPane.

JScrollBar – представляет собой отдельную полосу прокрутки (ползунок, область его движения и управляющие движением ползунка кнопки). Используется дополнительно для некоторых компонентов, если нет возможности использовать JScrollPane.

JSlider – представляет собой элемент для изменения значения в заданном диапазоне. Внешний вид компонента: шкала значений заданного диапазона (числа, изображения «засечек» или другие пиктограммы) и ползунок. Для управления ползунком используется перетаскивание мышью или клик на шкале ползунка. Для работы ползунка используется информация о минимальном значении, максимальном значении, текущем значении и внутреннем диапазоне.

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

JSpinner – представляет собой счетчик, который позволяет изменять текущее значение путем нажатия на соответствующие кнопки (увеличения и уменьшения значения). Внешне компонент похож на список JComboBox с одним выбранным элементом списка, но нажатие не раскрывает список, а изменяет значение в области отображения. Данное свойство счетчика позволяет использовать неограниченное количество элементов. Для счетчика JSpinner можно узнать его текущее, предыдущее и следующее значения, а также сменить текущее значение новым. Для обработки события изменения значения счетчика используется слушатель ChangeListener.

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

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

JTextPane – представляет собой многострочный текстовый редактор. Класс JTextPane является производным от класса JEditorPane. Обладая всеми возможностями своего базового класса, JTextPane добавляет к нему стилевую разметку текста. Компонент JTextPane используется в приложениях, где необходим многофункциональный текстовый редактор, который позволяет установить шрифт и его размер, цвет символов, выравнивание текста и т.п., использовать различные стили для выделения заголовков, основного текста, сносок и элементов текста.

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

Подход к обработке событий в Java основан на модели делегирования событий, определяющей стандартные и согласованные механизмы для гене-рации и обработки событий. При этом логика обработки события отделяется от логики пользовательского интерфейса, генерирующего эти события. Каждый элемент пользовательского интерфейса может «делегировать» обработку события отдельному фрагменту кода.

Концепция модели делегирования событий включает совместную работу трех объектов: источника, слушателя и события (рис.1).

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

Модель делегирования событий

Рис. 1. Модель делегирования событий

Под событием в данной модели понимается объект, инкапсулирующий изменение состояния источника. Событие генерируется в результате взаимодействия пользователя с элементом в графическом интерфейсе (например, клик на экранной кнопке, ввод символа с клавиатуры, выбор элемента в списке или клик кнопкой мыши).

Под источником в данной модели понимается объект, генерирующий событие. Генерация события происходит при изменении внутреннего состояния объекта. Источники могут генерировать более одного типа событий. Для обработки событий источник должен регистрировать слушателей. Каждый тип события имеет собственный метод регистрации. Общая форма регистрации слушателя:

public void addTypeListener(TypeListener el);

Где Type – это имя объекта события, el – ссылка на слушателя события. Например, addKeyListener() – регистрация слушателя событий клавиатуры, addMouseMotionListener() – регистрация слушателя движения мыши. Когда событие сгенерировано, все зарегистрированные слушатели получают копию объекта события. Это называется групповой рассылкой события.

Источник также может отменить регистрацию слушателя для определенного типа событий. Общая форма отмены регистрации слушателя:

public void removeTypeListener(TypeListener el);

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

Основные классы событий: MouseEvent, который генерируется при перетаскивании, перемещении, щелчках, нажатии и отпускании кнопок мыши, а также возникает, когда курсор мыши входит на компонент либо покидает его; KeyEvent, который генерируется при получении ввода с клавиатуры; ActionEvent, который генерируется при нажатии кнопки, двойном щелчке на элементе списка либо выборе пункта меню; ItemEvent, который генерируется при щелчке на флажке или элементе списка, при выборе элемента списка, отметке или снятии отметки пункта меню; MouseWheelEvent, который генерируется при прокрутке колесика мыши; TextEvent, который генерируется при изменении значения текстовой области или текстового поля; FocusEvent, который генерируется, когда компонент получает или теряет фокус клавиатурного ввода; WindowEvent, который генерируется при активизации либо закрытии окна, а также при деактивизации, свертывании, развертывании или выходе из него; ComponentEvent, который генерируется при сокрытии компонента, его перемещении, изменении его размера либо включении видимости; ContainerEvent, который генерируется при добавлении или исключении компонента из контейнера.

Основные интерфейсами слушателей являются MouseListener, KeyListener, ActionListener, ItemListener, MouseMotionListener, MouseWheelListener, TextListener, FocusListener, WindowFocusListener, WindowListener, ComponentListener и ContainerListener.

MouseListener содержит метод для обработки клика мыши (нажатия и отпускания кнопки мыши в одной и той же точке) mouseClicked(), метод для обработки события перехода мыши в область отображения компонента mouseEntered(), метод для обработки события перехода мыши из области отображения компонента mouseExited(), метод для обработки нажатия кнопки мыши mousePressed() и метод для обработки отпускания кнопки мыши mouseReleased(). Каждый метод этого слушателя принимает событие MouseEvent.

KeyListener содержит метод для обработки нажатия клавиши клавиатуры keyPressed(), метод для обработки отпускания клавиши клавиатуры keyReleased() и метод для обработки печати (ввода) символа (только для печатных символов) keyTyped(). Каждый метод данного слушателя принимает событие KeyEvent.

ActionListener содержит один метод для обработки события действия actionPerformed(), который принимает событие ActionEvent.

ItemListener содержит один метод для обработки события изменения состояния элемента itemStateChanged(), который принимает событие ItemEvent.

MouseMotionListener содержит метод для обработки перемещения курсора мыши mouseMoved() и метод для обработки перетаскивания курсора мыши mouseDragged(). Каждый метод этого слушателя принимает событие MouseEvent.

MouseWheelListener содержит один метод для обработки события прокрутки колесика мыши mouseWheelMoved(), который принимает событие MouseWheelEvent.

TextListener содержит один метод для обработки события изменения содержимого текстового поля или текстовой области textChanged(), который принимает событие ТехtEvent.

FocusListener содержит метод для обработки события получения компонентом фокуса клавиатурного ввода focusGained() и метод для обработки события потери компонентом фокуса клавиатурного ввода focusLost(). Каждый метод данного слушателя принимает событие FocusEvent.

WindowFocusListener содержит метод для обработки события получения окном фокуса ввода windowGainedFocus() и метод для обработки события потери окном фокуса ввода windowLostFocus(). Каждый метод данного слушателя принимает событие WindowEvent.

WindowListener содержит метод для обработки события активизации окна windowActivated(), метод для обработки события закрытия окна windowClosed(), метод для обработки события деактивизации окна windowDeactivated(), метод для обработки события разворачивания окна windowDeiconified(), метод для обработки события сворачивания окна в пиктограмму на панели задач операционной системы windowlconified(), метод для обработки события открытия окна windowOpened(). Каждый метод данного слушателя также принимает событие WindowEvent.

ComponentListener содержит метод для обработки события изменения размера компонента componentResized(), метод для обработки события перемещения компонента componentMoved(), метод для обработки события отображения компонента componentShown() и метод для обработки события сокрытия компонента componentHidden(). Каждый метод этого слушателя принимает событие ComponentEvent.

ContainerListener содержит метод для обработки события добавления компонента к контейнеру componentAdded() и метод для обработки события удаления компонента из контейнера componentRemoved(). Каждый метод данного слушателя принимает событие ContainerEvent.

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

Контрольные вопросы

 1. Для чего предназначены компоненты библиотеки Swing?

 2. Перечислите и опишите компоненты библиотеки Swing для управления текстовым вводом.

 3. Укажите назначение и опишите компоненты библиотеки Swing JButton, JToggleButton и его наследников.

 4. Перечислите и опишите компоненты библиотеки Swing для работы со списками элементов.

 5. Укажите назначение и опишите компоненты библиотеки Swing JSlider, JProgressBar и JSpinner.

 6. Опишите модель делегирования события Java, приведите схему, опишите назначение и роль объектов.

 7. Как осуществляется регистрация слушателя события?

 8. Перечислите основные классы событий Java.

 9. Перечислите основные интерфейсы слушателей Java и их методы?

10. Перечислите этапы использования модели делегирования событий.


Видео для подготовки

Исходные коды проекта из видео:



ЛАБОРАТОРНАЯ РАБОТА №3

Работа с массивами данных. Компонент JTable

Цель работы: Изучить концепцию Model-View-Controller (MVC) на примере компонента пользовательского интерфейса JTable. Изучить свойства и методы компонента JTable. Исследовать возможности компонента JTable для работы с массивами данных.

Методические указания к выполнению лабораторной работы

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

Независимо от того, какая архитектура используется для реализации компонента, она должна неявно включать эти три аспекта. Главной архитектурой в разработке компонентов с графическим интерфейсом остается архитектура «модель-представление-контроллер» (Model-View-Controller – MVC).

Успех архитектуры MVC объясняется тем, что каждый элемент дизайна соответствует некоторому аспекту компонента. Исходя из терминологии MVC, модель соответствует информации о состоянии, связанной с компонентом. Например, в случае флажка его модель содержит поле, которое показывает, отмечен ли флажок. Представление определяет, как компонент отображается на экране, включая любые аспекты представления, зависящие от текущего состояния модели. Контроллер определяет, как компонент будет реагировать на действия пользователя. Например, когда пользователь щелкает на флажке, контроллер реагирует изменением модели, чтобы отразить выбор пользователя (отметка флажка или снятие отметки). Это впоследствии приводит к обновлению представления. Разделяя компонент на модель, представление и контроллер, можно изменять конкретную реализацию любого из этих аспектов, не затрагивая остальные. Например, различные реализации представлений могут визуализировать один и тот же компонент разными способами, однако это не будет влиять на модель или контроллер.

Swing использует модифицированную версию MVC, которая объединяет представление и контроллер в один логический объект, называемый делегатом пользовательского интерфейса. В связи с этим подход, применяемый в Swing, называется или архитектурой «модель-делегат» (Model-Delegate), или архитектурой «разделяемая модель» (Separable Model).

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

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

JTable использует три модели, поставляющие ей данные и сохраняющие изменения при изменении этих данных. Первая и самая важная модель реализует интерфейс TableModel и хранит данные ячеек таблицы и дополнительную служебную информацию об этих ячейках. Вторая модель реализует интерфейс TableColumnModel и управляет столбцами таблицы. TableColumnModel позволяет добавлять, перемещать и находить столбцы, узнавать об изменениях в их расположении, с ее помощью можно изменить расстояние между столбцами и находящимися в них ячейками. Третья модель таблицы SelectionModel используется для выделения списка. Она управляет выделением строк таблицы и не затрагивает столбцы. Модель TableColumnModel имеет собственную модель выделения списка ListSelectionModel. Помимо моделей таблица JTable предоставляет еще несколько удобных механизмов выделения строк, столбцов и ячеек.

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

При работе с таблицами необязательно использовать модели. JTable включает конструкторы, в которые можно передать массивы или коллекции с информацией о ячейках таблицы и при необходимости о названиях ее столбцов. Для простых программ и незатейливых таблиц такой подход удобнее. Значение любой ячейки таблицы в этом случае можно получить с использованием метода getValueAt(), а изменить значение ячейки можно методом setValueAt().

Таблица JTable реализует интерфейс Scrollable и ее можно добавлять в панель прокрутки JScrollPane. При прокрутке полностью появляются следующий столбец или следующая строка таблицы. Заголовок таблицы, реализованный классом JTableHeader, будет виден только при помещении таблицы в панель прокрутки, поскольку таблица размещает компонент JTableHeader в виде заголовка панели прокрутки.

Модель данных таблицы TableModel позволяет подробно описать каждую ячейку таблицы JTable. Определенные в модели методы позволяют получить значение произвольной ячейки и изменить его, узнать о возможности редактирования ячейки, получить информацию о столбцах (количество, наименование, тип) и строках. Модель DefaultTableModel хранит переданные ей данные в двух векторах: в одном из векторов хранятся такие же векторы c данными строк, а во втором векторе хранятся названия столбцов таблицы. К недостаткам DefaultTableModel относят низкую производительность.

Модель столбцов таблицы TableColumnModel позволяет настраивать столбцы таблицы JTable: устанавливать размеры столбцов, менять их местами, настраивать объекты для отображения в столбце и для редактирования ячеек столбца, управлять заголовком столбца и т.д.

Таблица JTable отображает в интерфейсе порядок столбцов согласно параметрам, определенным в TableColumnModel. Каждый столбец таблицы представлен объектом TableColumn, c помощью которого указываются: размеры столбца (предпочтительный, максимальный и минимальный), индекс столбца, объекты для отображения и редактирования данных, хранящихся в столбце.

В JTable для хранения информации о столбцах по умолчанию используется стандартная модель DefaultTableColumnModel. Модель столбцов таб-лицы хранит список TableColumn и при смене какого-либо свойства столбца или его расположения оповещает об этом событии слушателей TableColumnModelListener.

Ячейки таблицы можно выделять разными способами: строками, столбцами, интервалами, единичными ячейками и т.д. Эту задачу решают модели выделения SelectionModel, которые хранят выделенные строки и столбцы таблицы. Следует отметить, что и для столбцов, и для строк таблицы имеются собственные модели выделения, представленные интерфейсом ListSelectionModel. Таким образом, таблица имеет две модели выделения. Первая модель хранится непосредственно в JTable и отвечает за выделение строк таблицы. Вторая модель отвечает за выделение столбцов таблицы и хранится в модели столбцов таблицы TableColumnModel.

По умолчанию в таблице и для строк, и для столбцов используется стандартная реализация интерфейса ListSelectionModel – класс DefaultListSelectionModel.

Например, приложение Java с JTable:

import javax.swing.*;
import java.awt.event.*;

public class TableMonth9 extends JFrame {
  private String holidays9[];
  private JScrollPane jScrollPane1;
  private JScrollPane jScrollPane2;
  private JTable jTable1;
  private JTextPane jTextPane1;

  public TableMonth9() {
    initComponents();
    holidays9 = new String[30];
    holidays9[10] = "1 сентября.\nДень знаний.\n";
    ...
    holidays9[12] = "13 сентября.\nДень программиста.\n";
    ...
  }   
  
  private void initComponents() {
    jScrollPane1 = new JScrollPane();
    jTable1 = new JTable();
    jScrollPane2 = new JScrollPane();
    jTextPane1 = new JTextPane();
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE );
    jTable1.setFont(new java.awt.Font("Consolas", 1, 24));
    jTable1.setModel(new javax.swing.table.DefaultTableModel(
      new Object [][] {
        { null,            null,            null,          null,          
          null,            new Integer(1),  new Integer(2)  }, 
        { new Integer(3),  new Integer(4),  newInteger(5), new Integer(6), 
          new Integer(7),  new Integer(8),  new Integer(9)  },
        { new Integer(10), new Integer(11), new Integer(12), new Integer(13), 
          new Integer(14), new Integer(15), new Integer(16) },
        { new Integer(17), new Integer(18), new Integer(19), new Integer(20), 
          new Integer(21), new Integer(22), new Integer(23) },
        { new Integer(24), new Integer(25), new Integer(26), new Integer(27), 
          new Integer(28), new Integer(29), new Integer(30) }
      },
      new String [] { "ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ", "ВС" }) {
        Class[] types = new Class [] {
          java.lang.Integer.class, java.lang.Integer.class,
          java.lang.Integer.class, java.lang.Integer.class, 
          java.lang.Integer.class, java.lang.Integer.class, 
          java.lang.Integer.class
        };
        boolean[] canEdit = new boolean [] {
          false, false, false, false, false, true, false
        };
        public Class getColumnClass(int columnIndex) {
          return types [columnIndex];
        }
        public boolean isCellEditable(int rowIndex, int columnIndex) {
          return canEdit [columnIndex];
        }
      });
    jTable1.setIntercellSpacing(new java.awt.Dimension(2, 2));
    jTable1.setOpaque(false);
    jTable1.setRowHeight(30);
    jTable1.setRowSelectionAllowed(false);
    jTable1.addMouseListener(new MouseAdapter() {
      public void mouseClicked(MouseEvent evt) {
        jTable1MouseClicked(evt);
      }
    });
    jScrollPane1.setViewportView(jTable1);
    jScrollPane2.setViewportView(jTextPane1);
    ...
    pack();
  } 
  private void jTable1MouseClicked(MouseEvent evt){                                     
    int i = jTable1.getSelectedRow();
    int j = jTable1.getSelectedColumn();
    Integer day = (Integer) jTable1.getValueAt(i, j);
    jTextPane1.setText(holidays9[day-1]);  
  }                                    
  public static void main(String args[]) {
    java.awt.EventQueue.invokeLater(() -> {
      new TableMonth9().setVisible(true);
    });
  } 
}

Контрольные вопросы

 1. Какими аспектами определяется компонент с графическим интерфейсом?

 2. Из каких элементов состоит архитектура MVC?

 3. Для чего предназначен контроллер в MVC?

 4. Какие модифицированные версии MVC используются в библиотеке компонентов Swing.

 5. Что инкапсулирует класс JTable?

 6. Какая модель компонента JTable отвечает за управление данными в ячейках таблицы?

 7. Какая модель компонента JTable управляет столбцами таблицы?

 8. Какая модель компонента JTable отвечает за выделение элементов таблицы?

 9. Где хранит данные модель DefaultTableModel?

10. Какие методы используются для получения доступа к ячейке таблицы?


Видео для подготовки

Исходные коды проекта из видео:



ЛАБОРАТОРНАЯ РАБОТА №4

Многопоточное программирование в Java. Синхронизация потоков в Java. Мониторы

Цель работы: Изучить особенности многопоточного программирования, методы и средства работы с потоками в Java. Исследовать средства синхронизации потоков в Java.

Методические указания к выполнению лабораторной работы

Процесс это совокупность кода и данных, разделяющих общее виртуальное адресное пространство. Чаще всего одна программа состоит из одного процесса.

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

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

Многопоточная система в Java построена на основе класса Thread и интерфейса Runnable. Класс Thread инкапсулирует поток исполнения. Чтобы создать новый поток, следует расширить класс Thread или реализовать интерфейс Runnable.

Поток можно создать из объекта любого класса, реализующего интерфейс Runnable. Для реализации интерфейса Runnable в классе должен быть объявлен единственный метод run(). В теле метода run() определяется код, который будет выполняться в отдельном потоке. После создания класса, реализующего интерфейс Runnable, в этом классе следует получить экземпляр класса Thread. Класс Thread содержит ряд конструкторов, использующих Runnable. Например: Thread(Runnable объект_потока), Thread(Runnable объект_потока, String имя_потока).

Поток можно создать также из любого класса наследованием класса Thread и переопределением его метода run(). Для создания потока из производного класса, можно воспользоваться, например, следующими конструкторами базового класса – Thread() или Thread(String имя_потока).

Запуск созданного потока осуществляется вызовом метода start(). Для управления потоком класс Thread также содержит методы: getName() для получения имени потока, setName() для задания имени потока, getPriority() для получения приоритета потока, setPriority() для задания приоритета потока, join() для ожидания завершения исполнения потока, isAlive() для определения состояния потока (выполняется ли данный поток), sleep() для приостановления выполнения потока на заданное время в миллисекундах.

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

В основе механизма синхронизации Java лежит понятие монитора. Монитор – это объект, используемый для взаимоисключающей блокировки потоков. Только один поток может в одно и то же время владеть монитором. Поток входит в монитор, когда запрашивает блокировку. Все остальные потоки, пытающиеся войти в заблокированный монитор, будут приостановлены до тех пор, пока первый поток не выйдет из монитора, то есть будут ожидать монитор.

Синхронизировать код можно двумя способами с использованием ключевого слова synchronized.

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

Второй способ использует «синхронизированные» объекты. Для синхронизации доступа к методам объектов, не предназначенных для многопоточного доступа (не синхронизированных), применяют оператор synchronized:

synchronized (ссылка_на_объект) {

  // синхронизированное использование

  // методов объекта

}

Например, приложение для параллельного построения графиков функций с заданным шагом в одной области отображения (общий ресурс):

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.*;

class GraphP implements Runnable {
  private Graphics g;
  private Color cl;
  private String nameFunc;
  private int yP;
  GraphP(Graphics g, String f, Color cl) {
    yP = 0;
    this.g = g;
    nameFunc = f;
    this.cl = cl;
    new Thread(this).start();
  }
  public void run() {
   for(int i = 0; i < 792; i++) {
    try {
      Thread.sleep(40+(new java.util.Random(42)).nextInt(5));
    } catch (InterruptedException ex) {}
    yP = (nameFunc.equals("SIN"))?yPointSin(i):yPointCos(i);
    drawPoint(i, yP, cl);
   } 
  }
  private void drawPoint(int x, int y, Color col) {
   synchronized(g) {
      g.setColor(col);
      g.fillOval(x, y, 4, 4);  
    }
  }
  private int yPointSin(int x) {
    return 200+(int)Math.round(200*Math.sin(x*0.02));
  }
  private int yPointCos(int x) {
    return 200+(int)Math.round(200*Math.cos(x*0.02));
  }
}

public class GraphThreadUI extends JFrame {
  private java.awt.Canvas canvas1;
  public GraphThreadUI() {
    initComponents();
  }
  private void initComponents() {
    canvas1 = new java.awt.Canvas();
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    canvas1.setBackground(new java.awt.Color(0, 0, 0));
    canvas1.addKeyListener(new java.awt.event.KeyAdapter() {
      public void keyPressed(java.awt.event.KeyEvent evt) {
        canvas1KeyPressed(evt);
      }
    });
    …
    pack();
  }
  private void canvas1KeyPressed(java.awt.event.KeyEvent evt) {
  int key = evt.getKeyCode();
    int x, y;
    if(key == evt.VK_SPACE) {
      Graphics g = canvas1.getGraphics();
      g.setColor(Color.WHITE);
      GraphP gt1 = new GraphP(g,"SIN",new Color(124,124,124));
      GraphP gt2 = new GraphP(g,"COS",Color.RED);
    }
  }                                  

  public static void main(String args[]) {
    …
    java.awt.EventQueue.invokeLater(new Runnable() {
      public void run() {
        new GraphThreadUI().setVisible(true);
      }
    });
  }
}

Контрольные вопросы

 1. Объясните понятия процесса и потока.

 2. Что включает в себя контекст потока?

 3. На чем построена многопоточная система Java?

 4. Какими способами можно создать поток в Java?

 5. Какие конструкторы используются при создании объекта Thread?

 6. Какие методы содержит класс Thread для управления потоком?

 7. Какие проблемы могут возникнуть при использовании потоками программы общих ресурсов?

 8. Объясните понятие монитора?

 9. Как используется ключевое слово synchronized?

10. Приведите пример синхронизации по объекту.


Видео для подготовки

Исходные коды проекта из видео:



ЛАБОРАТОРНАЯ РАБОТА №5

Сетевое программирование в Java. Работа с классами Socket и ServerSocket

Цель работы: Изучить особенности сетевого программирования Java, методы и средства работы с клиентскими и серверными сокетами в Java.

Методические указания к выполнению лабораторной работы

В основе работы в сети, поддерживаемой в Java, находится понятие сокета, инкапсулирующего конечную точку в сети. Сокет позволяет отдельному компьютеру (серверу) одновременно предоставлять доступ к сервисам множеству других компьютеров в сети (клиентов). Это достигается благодаря использованию порта. Сервер «прослушивает» порт до тех пор, пока клиент не соединится с ним. Для управления соединениями со многими клиентами сервер должен быть многопоточным.

Связь между сокетами устанавливается и поддерживается стеком протоколов TCP(UDP)/IP. Протокол TCP резервирует первые 1024 порта для отдельных прикладных протоколов. Например, некоторые служебные порты:

  • порт 21 – для файлового протокола FTP;
  • порт 23 – для протокола Telnet;
  • порт 25 – для протокола электронной почты SMTP;
  • порт 43 – для службы whois;
  • порт 80 – для протокола гипертекста HTTP.

Для инкапсуляции IP-адреса (как числового его представления, так и соответствующего ему доменного имени) используется класс InetAddress. Этот класс может оперировать адресами как по протоколу IPv4, так и по протоколу IPv6. В классе InetAddress отсутствуют доступные конструкторы. Чтобы создать объект, следует использовать один из доступных в нем фабричных методов. Фабричный метод – статический метод, возвращающий экземпляр класса.

Фабричные методы класса InetAddress: getLocalHost(), который получает адрес локального хоста; getByName(), который получает адрес по имени хоста, указанного параметром; getAllByName(), который получает все адреса конкретного хоста, указанного параметром. Данные фабричные методы могут вызвать исключение UnknownHostException.

Основные методы класса InetAddress: getAddress(), который использу-ется для получения IP-адреса в виде массива байт; getHostAddress(), который используется для получения IP-адреса хоста в строковом виде; getHostName(), который используется для получения доменного имени хоста.

В Java поддерживаются две разновидности сокетов по протоколу TCP/IP: класс ServerSocket для инкапсуляции сервера, класс Socket для инкапсуляции клиента. Класс ServerSocket ожидает подключения клиентов для выполнения заданных действий (обрабатывает запросы). Класс Socket служит для подключения к серверным сокетам и инициирования обмена данными по сетевому протоколу.

Конструкторы класса Socket: Socket(String hostName, int port), Socket(InetAddress address, int port). Могут вызывать исключения UnknownHostException и IOException.

Методы для получения информации о сокете: getInetAddress(), используемый для получения связанного с сокетом адреса; getPort(), используемый для получения связанного с сокетом порта.

Для доступа к потокам ввода/вывода, связанным с сокетом, используют следующие методы: getInputStream() для получения потока байтового ввода InputStream и getOutputStream() для получения потока байтового вывода OutputStream. Данные методы могут вызвать исключение IOException. Объекты InputStream и OutputStream можно использовать для организации форматированного ввода/вывода – создания объектов DataInputStream и DataOutputStream, передавая их конструкторам байтовые потоки ввода/вывода.

Методы класса DataOutputStream: writeDouble() – для записи в поток данных типа double, writeBoolean() – для записи в поток данных типа boolean, writeInt() – для записи в поток данных типа int, writeUTF() – для записи в поток данных типа String.

Методы DataInputStream: readDouble() – для чтения из потока данных типа double, readBoolean() – для чтения из потока данных типа boolean, readInt() – для чтения из потока данных типа int, readUTF() – для чтения из потока данных типа String.

Класс ServerSocket применяется для создания серверов, которые принимают запросы от клиентских программ, устанавливающих соединение с ними через открытые порты. Конструкторы класса ServerSocket с указанием порта port и максимального количества подключаемых клиентов max:

ServerSocket(int port) throws IOException;

ServerSocket(int port, int max) throws IOException;

Метод accept() класса ServerSocket реализует блокирующий вызов, чтобы ожидать от клиента начала соединения. После соединения метод возвращает объект типа Socket, который используется для взаимодействия с клиентом.

Например, приложение, реализующее клиентский сокет для отправки текстового сообщения на сервер и получения ответного сообщения:

import java.net.*;
import java.io.*;
import java.awt.event.*;

public class ClientFrame extends javax.swing.JFrame {
  Socket client;  
  ChatSocket cs;
  String lastMessage = "";
  ... //элементы GUI
  
  public ClientFrame() {
    initComponents();
  }

  class ChatSocket implements Runnable {
    DataInputStream in;
    DataOutputStream out;
    boolean work;
    String name;
        
    ChatSocket(InputStream is, OutputStream os, String name) {
      in = new DataInputStream(is);
      out = new DataOutputStream(os);
      work = true;
      this.name = name;
      Thread t = new Thread(this);
      t.start();
    }
        
    private synchronized void add(String s) {
      jTextArea1.append(s + "\n");    
    } 
        
    public void stopListen() {
      work = false;
    }
        
    public void sendToServer(String s) {
      try {
        s = name + ": " + s;
        lastMessage = s;
        add(s);
        out.writeUTF(s);
        out.flush();    
      } catch(IOException e){};    
    } 
      
    public void run() {
      try {
        String line = null;
        while(work) {
          line = in.readUTF();
          if(line.compareTo(lastMessage) != 0) add(line);
        }
      } catch(IOException e){};
    }
  }
   
  private void initComponents() {
    ... // формирование GUI приложения
  } 

  private void jButton1ActionPerformed(ActionEvent evt) {
    try {
      String s = jFormattedTextField1.getText();
      InetAddress ia = InetAddress.getByName(s);
      s = jFormattedTextField2.getText();      
      int port = Integer.parseInt(s);
      client = new Socket(ia, port);
      String name = jTextField2.getText();
      jTextArea1.append("Подключение установлено.\n");
      InputStream in = client.getInputStream();
      OutputStream out = client.getOutputStream();
      cs = new ChatSocket(in, out, name);
    } catch(IOException e){};
  }                                        

  private void jTextField1ActionPerformed(ActionEvent evt) {
    cs.sendToServer(jTextField1.getText());
    jTextField1.setText("");
  }                                           

  public static void main(String args[]) {
    ...
    java.awt.EventQueue.invokeLater(new Runnable() {
      public void run() {
        new ClientFrame().setVisible(true);
      }
    });
  }
}

Контрольные вопросы

 1. Объясните понятие сокета.

 2. Какой стек протоколов используется для связи между сокетами?

 3. Приведите примеры служебных портов.

 4. Что инкапсулирует класс InetAddress?

 5. Какие разновидности сокетов поддерживается в Java?

 6. Какие конструкторы используются для создания объектов Socket?

 7. Какие методы используются для доступа к потокам ввода/вывода, связанным с сокетом?

 8. Какие объекты потоков используются для организации форматированного ввода/вывода?

 9. Какие конструкторы используются для создания объектов Server-Socket?

10. Какой метод используется для ожидания подключения клиентов в классе ServerSocket?


Видео для подготовки

Исходные коды проекта из видео:



ЛАБОРАТОРНАЯ РАБОТА №6

Коллекции Java. Объектная декомпозиция предметной области

Цель работы: Применить объектную декомпозицию к выбранной предметной области. Изучить возможности использования в разработке Java Collections Framework.

Методические указания к выполнению лабораторной работы

В данной работе необходимо разработать приложение для выбранной предметной области с использованием JCF (Java Collections Framework). Для этого рекомендуется выполнить следующие действия:

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

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

Большинство задач не предполагает ограниченное число объектов с фиксированным временем жизни. Для управления неограниченным количеством объектов (созданием, хранением, удалением) Java содержит набор классов контейнеров (в библиотеке java.util), например, List, Set, Queue, Map. Эти типы объектов также называются классами коллекций. Классы коллекций способны автоматически изменяться в размерах.

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

Например, чтобы определить ArrayList для хранения объектов String, следует использовать запись ArrayList<String>. В угловые скобки заключаются параметры-типы (их может быть несколько), определяющие типы, которые могут храниться в данном экземпляре контейнера.

Коллекции Java рассматривает вопрос хранения объектов как совокупность двух концепций, выраженных основными интерфейсами библиотеки: Коллекция (Collection) и Карта (Map).

Коллекция – последовательность отдельных элементов, формируемая по некоторым правилам. Интерфейс List (список) хранит элементы в определенной последовательности. В интерфейсе Set (множество) нельзя хранить повторяющиеся элементы. Интерфейс Queue (очередь) выдает элементы в порядке, определяемом дисциплиной очереди (обычно совпадающем с порядком вставки). Очереди часто используются для надежного перемещения объектов из одной области программы в другую.

Карта – набор пар объектов «ключ-значение» с возможностью выборки значения по ключу. Если контейнер ArrayList позволяет получить объект по числу, то карта позволяет получить объект по другому объекту. Также для контейнеров Map используют термин ассоциативный массив (объекты ассоциируются с другими объектами) и словарь (по аналогии поиска определения (объекта-значения) по слову (объекту-ключу)).

Интерфейс Collection обобщает концепцию последовательности – способа хранения группы объектов. Однако использовать восходящее преобразование к базовым интерфейсам необходимо с учетом того, что интерфейсы и классы обладают дополнительной функциональностью. Например, LinkedList содержит методы, отсутствующие в интерфейсе List, а TreeMap содержит методы, не входящие в интерфейс Map. Или, например, метод add() для интерфейса Collection добавляет в контейнер только те элементы, которые в нем отсутствовали (функциональность Set).

Для добавления групп элементов в Collection используются методы классов Arrays и Collections. Метод Arrays.asList() получает массив или список элементов переменной длины, который преобразуется в объект List. Метод Collections.addAll() получает объект Collection и либо массив, либо список элементов переменной длины, который добавляется в Collection.

Контейнер List гарантирует хранение списка элементов в определенной последовательности. Интерфейс List добавляет в Collection методы вставки и удаления элементов в середине списка. Существует две разновидности List: ArrayList и LinkedList.

Базовый контейнер ArrayList обладает отличной скоростью произвольного доступа к элементам, но относительно медленными операциями вставки и удаления элементов в середине. Связанный список LinkedList обладает оптимальным последовательным доступом и низкозатратными операциями вставки и удаления в середине списка, но относительно медленными операциями произвольного доступа. В LinkedList добавлены методы, которые позволяют использовать его как стек, очередь или дек (двустороннюю очередь).

Например, приложение, демонстрирующее возможности работы со списками ArrayList:

import java.util.*;

public class ListFeatures {
 public static void main(String[] args) {
  List l = new ArrayList();
  Collections.addAll(l, "массив", "карта", "список", "очередь", "карта", "стек");
  System.out.println("1: " + l);
  String a = new String("множество");
  l.add(a);
  System.out.println("2: " + l);
  System.out.println("3: " + l.contains(a));
  l.remove(a);
  String b = l.get(2);
  System.out.println("4: " + b + " " + l.indexOf(b));
  String c = new String("дек");
  System.out.println("5: " + l.indexOf(c));
  System.out.println("6: " + l.remove(c));
  System.out.println("7: " + l);
  String d = new String("очередь");
  System.out.println("8: " + l.indexOf(d));
  System.out.println("9: " + l.remove(d));
  System.out.println("10: " + l);
  System.out.println("11: " + l.remove(b));
  System.out.println("12: " + l);
  l.add(3, new String("множество"));
  System.out.println("13: " + l);
  List addl = new ArrayList(Arrays.asList("очередь", "список", "дек"));
  l.addAll(addl);
  System.out.println("14: " + l);
  List subl = l.subList(3, 7);
  System.out.println("Подсписок: " + subl);
  System.out.println("15: " + l.containsAll(subl));
  Collections.sort(subl);
  System.out.println("Подсписок после сортировки: " + subl);
  System.out.println("16: " + l.containsAll(subl));
  Collections.shuffle(subl, new Random(42));
  System.out.println("Подсписок после перемешивания: " + subl);
  System.out.println("17: " + l.containsAll(subl));
  List copyl = new ArrayList(l); 
  subl = Arrays.asList(l.get(1), l.get(5));
  System.out.println("Подсписок: " + subl);
  copyl.retainAll(subl);
  System.out.println("18: " + copyl);
  copyl = new ArrayList(l);
  copyl.remove(2);
  System.out.println("19: " + copyl);
  copyl.removeAll(subl);
  System.out.println("20: " + copyl);
  copyl.set(1, new String("карта"));
  System.out.println("21: " + copyl);
  copyl.addAll(2, subl);
  System.out.println("22: " + copyl);
  System.out.println("23: " + l.isEmpty());
  l.clear();
  System.out.println("24: " + l);    
  System.out.println("25: " + l.isEmpty());
  l.addAll(copyl.subList(0, 5));
  System.out.println("26: " + l);  
  Object[] o = l.toArray();
  System.out.println("27: " + o[3]);      
  String[] sa = l.toArray(new String[0]);
  System.out.println("28: " + sa[3].toUpperCase());
 }
}

Set представляет собой разновидность Collection, реализуя поведение, отличное от поведения List. Контейнер Set сохраняет не более одного экземпляра каждого объекта-значения, то есть предотвращает появление дубликатов. Разновидности Set:

  • HashSet – использует для размещения элементов хеширование (увеличивается скорость доступа);
  • LinkedHashSet – также использует хеширование для повышения скорости поиска, но элементы «сохраняются» в связанном списке в порядке вставки;
  • TreeSet – хранит элементы отсортированными в специальной структуре данных – «красно-черном дереве».

Самая частая операция, выполняемая с множествами, является проверка присутствия значений методом contains().

Главной функциональностью контейнера Map является возможность отображения одних объектов на другие объекты. Объект Map чаще других используется для создания сложных вложенных контейнеров. Разработчик в таком случае получает возможность быстро организовать любую нетривиальную структуру данных.

Часто необходим код для работы с контейнером, не зависящий от его конкретного типа. Для решения данной проблемы используется концепция итератора (iterator). Итераторы унифицируют доступ к контейнерам.

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

Интерфейс Iterator поддерживает перемещение только в одном направлении. Операции, доступные итератору:

  • запрос у Collection итератора посредством метода iterator(). Этот итератор готов вернуть первый элемент последовательности;
  • получение следующего элемента последовательности вызовом метода next();
  • проверка наличия следующего объекта в последовательности методом hasNext();
  • удаление из последовательности элемента, возвращенного итератором последним, методом remove().

Интерфейс ListIterator является двусторонним, но работает только с классами List.

Контрольные вопросы

 1. В чем заключается объектная декомпозиция предметной области задачи?

 2. Для чего используются коллекции Java?

 3. Укажите особенности интерфейсов Collection и Map.

 4. Как добавить группу элементов в коллекцию типа Collection?

 5. Какие разновидности коллекций List есть в JCF?

 6. Какие разновидности коллекций Set есть в JCF?

 7. Для чего используются коллекции типа Map?

 8. Что используют для унифицированного доступа к контейнерам?

 9. Какие операции доступны итератору типа Iterator?

10. Укажите, что будет выведено при выполнении заданного фрагмента программы работы с коллекциями ArrayList, приведенной выше? Объясните, для чего используются методы в данном фрагменте?



Количество правильных ответов:

Результаты:




СПИСОК ЛИТЕРАТУРЫ

 1. Шилдт, Г. Swing. Руководство для начинающих. – М.: Вильямс, 2007. – 704 с.: ил.

 2. Шилдт, Г. Java 8. Полное руководство. – 9-е издание – М.: Вильямс, 2015. – 1376 с.: ил.

 3. Эккель, Б. Философия Java. – 4-е полное издание – СПб.: Питер, 2016. – 1168 с.: ил.

 4. Справочная система компании Oracle. Материалы сайта https://docs.oracle.com/ – Режим доступа: https://docs.oracle.com/




СВЕДЕНИЯ О ПОСОБИИ