Задания

Л.р.№1 Файлы

Есть информация о турпоездке:

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

Level one (2 балла)

  • Сделайте (качественный!) класс "турпоездка".
  • Не реализуйте (проигнорируйте) поля "дата отъезда"/"дата приезда" (без них проще).
  • Заполните в коде массив объектов ("турпоездок") и выгрузите его в текстовый файл.
  • Затем прочитайте туры из файла в другой массив и выведите их на экран.

Level two (+1 балл)

  • То же, но с датами. Используйте java.util.Date для хранения даты, java.util.GregorianCalendar и java.text.SimpleDateFormat для преобразования в строку/из строки (помните о методе getDate() в Calendar'е!).

Level three (5 баллов всего)

1. Реализуйте меню для пользователя со следующими возможностями:
  • просмотр информации обо всех имеющихся турах;
  • добавление тура (всегда в конец);
  • удаление тура в заданной позиции (со сдвигом);
  • редактирование тура в заданной позиции.
2. При запуске программа считывает туры из текстового файла на диске. При завершении - записывает в этот файл измененный список туров (файл "перезаписывается заново").

Проверьте свою программу по чеклисту - особенно обращаю ваше внимание на п.2.2!

Level four (бонус: +4 балла сверху!)

В дополнение к заданию "level three": сделать еще одну его - модифицированную  - версию, использующую бинарный файл ВМЕСТО текстового. Запись в файл можно реализовать через DataOutputStream и RandomAccessFile.

ВАЖНО! Обязательно предусмотрите возможность работы с очень большими файлами (которые не помещаются в ОЗУ). Для этого не храните все туры в массиве, а обращайтесь каждый раз прямо к файлу: читайте его в цикле для вывода на экран, делайте seek() в для добавления, удаления и редактирования записей. (при удалении - НЕ НАДО делать сдвиг всех последующих элементов - просто помечайте элемент в файле как "удаленный" (в нумерации записей при этом появятся "дырки") - кстати, именно так работают с файлами реальные СУБД).

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

  1. Приведите код, записывающий в текстовый файл 2 объекта класса Студент (имя, курс) и код, считывающий их обоих обратно.
  2. Будет ли он работать, если поле "имя" содержит пробелы? А если поменять местами имя и курс?
  3. Какая в вышеупомянутом упражнении используется кодировка символов? Как ее изменить?
  4. Объясните разницу между текстовыми и бинарными файлами.
  5. Как записать "студента" в бинарный файл? (и как прочитать обратно)
  6. Объясните основные принципы работы с RandomAccessFile. Зачем этот класс вообще придумали, если и без него есть текстовые и двоичные потоки?
  7. В какой директории должен находиться файл, если при его открытии указывать только имя (и не указывать путь)?
  8. Почему в ОС Windows директории в пути нужно разделять двумя бэкслешами, а не одним: "c:\\windows\\lalala.txt"?
вопросы и комментарии писать тут

Л.р.№2 Сеть

Задание 1 (3 балла)

Напишите небольшую программу (или две программы - клиент и сервер) "односторонний чат". В ней будет две функции main. Первая (сервер) - создает ServerSocket и принимает на нем входящие соединения (многопоточность не требуется!). Вторая (клиент) - через клиентский Socket подключается к первой, предлагает пользователю ввести сообщение и нажать enter - после чего отправляет полученную строку через сетевое соединение. В коде программы должна быть возможность задать используемую кодировку символов! Сервер, получив через сокет очередную строку выводит ее на экран. Помимо самого сообщения сервер должен выводить на экран адрес и порт того, кто его отправил (см. документацию на класс Socket) (1 балл).

Не забывайте своевременно делать flush()!

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

Задание 2 (2 балла)

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

Для этого добавьте в л.р.1 еще два пункта меню:

  • отправить одну запись по указанному адресу (указывается ip-адрес и порт);
  • получить запись с другой машины (указывается локальный порт).

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

Задание 3 (бонус: +4 балла сверху)

Упражнение для понимания буферизации.

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

  • предварительное "установление соединения" (т.е. возможность указать адрес назначения пакетов один раз - вместо того, чтобы делать это при каждой отправке);
  • отправку потока байтов (через методы класса OutputStream, которые у вас реализованы по-своему в специальном классе, объект которого можно получить у вашего сокета путем вызова getOutputStream);
  • получение потока байтов (через методы класса InputStream, которые у вас реализованы по-своему в специальном классе, объект которого можно получить у вашего сокета путем вызова getInputStream);

Общая идея реализации - получаемые для отправки данные складываются в буфер, который целиком отправляется одним дейтаграммным пакетом, когда заполнится до определенной величины (1-2 килобайта). И наоборот - байты из полученных по сети пакетов складываются в буфер, из которого их можно читать методами класса InputStream (кстати, для обеих задач хорошо подходят библиотечные классы ByteArrayInputStream и ByteArrayOutputStream). И не забудьте о методе flush().

Осторожно! "Настоящий" Socket работает с (довольно сложным) протоколом TCP - который обеспечивает гарантированную доставку всех байтов (причем в правильном порядке - в отличие от DatagramSocket) и двустороннее установление соединения (т.е. обе стороны знают, что соединение установлено и с кем). От вашей реализации ничего такого не требуется! Ваша задача - вместо методов send(packet) и receive(packet) сделать методы write(byte[]) и read(byte[]). Причем, если при получении запрошено больше байтов, чем имеется в наличии - то программа должна ждать, пока они все будут получены (т.е. просто в цикле вызывать receive(), пока в буфере не накопится нужное количество байт).

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

  1. Как отправить или получить данные из сокета? (не используя никакие вспомогательные классы)?
  2. Как писать/читать из сокета в заданной кодировке?
  3. Как отправить или получить из сокета бинарные данные?
  4. Как будет вести себя программа, если на стороне отправителя не сделать flush? Почему?
  5. В чем проблема сделать этот чат двусторонним?

вопросы и комментарии писать тут

Л.р. №3 Потоки

Задание 1 (2 балла)

Закодить пример программы, моделирующей транзакции в банке, из 2-го тома Хорстманна без блокировок. Показать, что программа действительно работает неправильно, объяснить почему и как это исправить.

Задание 2 (3 балла)

Реализовать многопоточную программу с якобы "тяжелыми вычислениями". В качестве "тяжелых вычислений" производить вычисление числа Пи с помощью ряда Лейбница: 4*(1-1/3+1/5-1/7+1/9-...). Примечание: этот ряд очень плохо сходится, поэтому чтобы достичь более-менее приличной точности, надо очень долго считать.

Программа предоставляет пользователю (в консоли) меню следующего вида:

  1. Продолжить вычисления.
  2. Приостановить вычисления.
  3. Посмотреть текущий результат.
  4. Узнать суммарное время, затраченное на вычисления.
  5. Выход.
Собственно вычисление ряда выполняет отдельный поток (в бесконечном цикле). Главный поток может его приостановить (для этого нужно заставить вычислительный поток выполнить вызов wait()), продолжить (notify()), показать текущий достигнутый результат вычислений (собственно, число Пи, но пока не очень точное). При приостановке загруженность процессора (в "диспетчере задач") должна падать  до небольшого значения около 0, при продолжении - возрастать до 100%. Когда вычисления приостановлены, операция "посмотреть текущий результат" при многократном ее выполнении должна показывать одно и то же (потому что текущее значение Пи не меняется); во время вычислений - соответственно - при каждом повторном просмотре Пи должно становиться все точнее и точнее.

Операция "узнать суммарное время" - показывает сколько секунд проработал вычислительный поток. Т.е., когда он "спит" в режиме паузы - эта величина не меняется; когда работает - увеличивается со скоростью "одна секунда в секунду".

В качестве примера см. ниже NewThread.jar (из консоли запускается командой "java -jar NewThread.jar"). Надо сделать такое же, но без окошек!

На что обратить внимание:
  • Разберитесь, какому классу принадлежат методы wait() и notify() и какой именно поток они приостанавливают/пробуждают. Что такое notifyAll()?
  • Что такое  synchronized, какую проблему оно решает. Помните, что бывают synchronized-методы и synchronized-секции. (примечание: IllegalThreadStateException - это не искомая проблема, это ее следствие).
  • Зачем у synchronized-блока есть параметр-объект? ("synchronized(объект){код}")
  • Как правильно останавливать потоки. Метод join().
  • Литература - Шилдт и Хорстманн Т2.

вопросы и комментарии писать тут

Л.р. №4 Анализ требований + немного GUI (5 баллов + бонус)

Освежите в памяти основные этапы проектирования ПО (и нашего ChatApp) по ЛК04.2.

Ознакомтесь с концепцией проекта и его итерациями по ТЗ (см. chatapp-doc).

ВНИМАНИЕ! В документе с ТЗ разрешены комментарии! Если в документе что-то непонятно - задайте вопрос в комментарии на полях (для этого в папке нажать "open in drive", а в документе - insert->comment).

Задания:

  1. Скачайте (или скопируйте себе на гугль-драйв) документ ТЗ. Полностью заполните в нем разделы "Итерация 1.1" и "Итерация 1.2": пропишите все необходимые user stories, выделите из них use-case'ы и опишите их письменно, а также изобразите их на use-case диаграмме (итого: по одной use-case диаграмме и одному макету UI на каждую из двух итераций). Для рисования используйте (например) creately. Макет UI, изображенный в ТЗ можно заменить своим - который будет вам больше нравиться. ВАЖНО! Цель всех этих действий - получить непротиворечивое и полное описание функциональности программы. Поэтому мысленно "прогоните" все свои user stories по макету UI и по use-case диаграмме: так вы сможете обнаружить на макете и диаграмме либо нехватку элементов, либо, наоборот, лишние элементы (что влияет на оценку).
  2. (бонус: +3 балла) Покопайтесь в интернете и "запрограммируйте" разработанные на предыдущем шаге макеты UI в виде java-классов (не работающих, но рисующих все необходимые элементы управления, заполненные "тестовыми" данными). Используйте для этого либо layout manager'ы, либо построитель интерфейсов NetBeans (или WindowBuilder в Eclipse, или GUI Designer в IntelliJ IDEA).

Л.р. №5 GUI (5 баллов)

  1. Сделайте программу, которая рисует на панели "Hello, World" (Хорстманн, т.1, глава 7 "Программирование графики").
  2. Сделайте программу, которая при нажатиях на кнопки меняет цвет панели (Хорстманн, т.1, глава 8 "Обработка событий").
  3. Почитайте в главе 9 Хорстманна ("Компоненты пользовательского интерфейса из пакета Swing") про layout manager'ы ("Общие сведения о компоновке элементов"): FlowLayout, BorderLayout, GridLayout (там, где пример с калькулятором). Также советую погуглить про BoxLayout.
  4. Собственно, основное задание (предыдущие - для тренировки): на "фрейме", у верхнего края, статическая надпись, под ней - картинка. Еще ниже - 4 кнопки: «Доброе утро», «Добрый день», «Добрый вечер», «Доброй ночи». При нажатии на одну из кнопкок - меняется надпись и картинка (в соответствии с тем, какую кнопку нажали).
  5. Вторая часть задания: добавить к п.4 три CheckBox'а для изменения цвета панели (фон всего окна) и два RadioButton'а для изменения цвета текста надписи (наверное, пригодится BorderLayout; также - подумайте, как с помощью трех CheckBox'ов задавать 8 цветов).

ChatApp итерация 1.1

Материалы:

  1. ТЗ, протокол и архитектура
  2. Репозиторий на Github, который надо форкнуть
  3. Javadoc с детальным описанием каждого класса (к соблюдению совершенно не обязательно! Просто это один из вариантов, как можно было бы сделать)

Порядок действий примерно следующий (опять же - это рекомендация, а решаете вы сами):

  1. Программируем классы (предметной области) Connection и Command. Вместе они реализуют обмен командами согласно протоколу. Примечание: чтобы перейти к следующим шагам, не обязательно доделывать эти классы до конца!
  2. Тестируем классы Connection и Command - пишем пару main'ов которые будут на одной стороне отсылать разные типы команд, а на другой - проверять, что команда принята и распознана правильно. Также полезно будет протестировать взаимодействие вашей реализации этих классов с чьей-либо еще.
  3. Программируем CallListener. Тестируем его реакцию на входящее подключение в двух режимах: busy и не-busy (для этого используем класс Connection).
  4. Отдельный участник команды может заняться классом Caller. Потом протестировать его совместно с CallListener.
  5. Программируем главную форму (можно начать это сразу после п.1!). Сначала она будет уметь только отправлять сообщения, потом звонить (но не принимать звонки и входящие сообщения).
  6. Разбираемся с паттерном "наблюдатель". Читаем о соответствующем классе в Java. Javadoc: Observer, Observable.
  7. Реализуем CallListenerThread и CommandListenerThread (с потоками и наблюдателями). Пишем небольшой main, который их протестирует!
  8. Прикручиваем CallListenerThread и CommandListenerThread к главной форме с  помощью наблюдателей. Обработку событий от этих классов перенаправляем в главный поток обработки событий с помощью SwingUtilities.invokeLater().
  9. Доделываем недоделанные ранее мелочи (например, подтверждение пользователем входящего звонка, класс Caller). Проверяем, ничего ли мы не забыли, по use-case'ам и историям из ТЗ!
  10. Тестируем полученную программу по use-case'ам и историям из ТЗ. Лучше всего это поручить постороннему человеку, не участвовавшему в разработке!

Оценивание:

 Баллы   За что
 6
Коммиты 3 недели в git.
 2Программу можно назвать чатом: работает подключение и беседа.
 1Срабатывает кнопка disconnect, и после нее опять можно подключаться.
 1 Реализовано accept/reject.
 1 Реализован ответ "busy".
 1 В списке сообщений на формочке есть прокрутка.

ChatApp итерация 2

Срок

Две недели - до 3 декабря включительно.

Минимальные требования

  1. Запускаемый jar-ник программы выложен в качестве "релиза" на github.
  2. В программе имеется список "контактов", которым можно как-либо адекватно пользоваться.
  3. В программу интегрирован класс ServerConnection и с помощью него она оставила запись о себе в базе на сервере.

Остальные требования

  1. Постройте компонент для списка контактов по модели MVC. Также желательно по этой модели построить виджет с историей сообщений, а еще лучше - все главное окно программы.
  2. Сделайте сохранение контактов на диск.
  3. Дополните документ "Архитектура" всеми своими классами.
  4. Проверьте программу на соответствие всем пунктам ТЗ (добавив всю недостающую функциональность!). Программа, ТЗ и "архитектура" должны точно друг другу соответствовать!
  5. Хорошо протестируйте свою программу! Вы найдете в ней довольно много багов. Выделите достаточно много времени на их устранение!

Оценивание

 Баллы   За что
 4
Коммиты 2 недели в git.
 2Выполнены "минимальные требования" (см. выше).
 4Реализовано нормальное MVC.
 3"Архитектура" и ТЗ приведены в соответствие с программой (и наоборот!).
ċ
NewThread.jar
(4k)
Dima Litvinov,
8 сент. 2015 г., 4:38
ċ
ServerConnection.java
(6k)
Dima Litvinov,
22 нояб. 2015 г., 14:52
ĉ
Dima Litvinov,
8 сент. 2015 г., 4:38
Comments