Лекции

  1. Лекция 1. Коллекции в Java
  2. Лекция 2. Ассоциативные массивы и абстрактные классы
  3. Лекция 3. Интерфейсы и лямбда выражения
  4. Лекция 4. Использование лямбда выражений, JavaFX
  5. Лекция 5. JavaFX Сцена
  6. Лекция 6. Ограничения
  7. Лекция 7. Слушатели, наблюдаемые значения
  8. Лекция 8. Связывания наблюдаемых значений
  9. Лекция 9. Связывания наблюдаемых значений
  10. Лекция 10. Исключения
  11. Лекция 11. Рисование фигур, код
  12. Лекция 12. Класс Object,
  13. Лекция 13. Многопоточное программирование, код

Задачи для практических занятий

Объектно-ориентированное программирование

Класс “Время”

Дедлайн: 3 марта

пакет: ru.spbu.arts.java.oop.time

Вы должны создать класс ExperimentsWithTime, чтобы в нем проверять всё, что вы сделаете с часами. Нужно проверить работу каждого пункта, который вы выполните. Пожалуйста, в окончательной версии не ставьте комментарии на куски кода, которые проверяют работу вашего класса, обычно так делают, чтобы старые проверки не мешали новым. Так вот, не надо комментировать проверки.

  1. Заведите класс Time, который хранит информацию о количестве часов и минут. Т.е. введите два поля.
  2. Добавьте конструктор, в котором можно сразу указать количество часов и минут.
  3. Сделайте метод String show();, который возвращает строку с часами и минутами через двоеточие. Например, он может вернуть "09:10".
  4. Сделайте методы isDay(), isMorning(), isEvening(), isNight(), которые возвращают верно ли, что сейчас, соответственно, день, утро, вечер, ночь. Придумайте сами, начиная с какого времени утро переходит в день и т.п.
  5. Сделайте метод String sayHello(), который возвращает строку “Доброе утро”, “Добрый день” и т.п. в зависимости от текущего времени.
  6. Сделайте метод, который добавляет указанное количество минут: c.add(20). Если было "9:10", то должно получиться "9:30". А если было "9:50", должно получиться "10:10". Еще пример. "9:20" плюс 120 минут должно быть "11:20".

ASCI-графика

Дедлайн: 3 марта

пакет: ru.spbu.arts.java.oop.ascigraphics

Класс Drawing (изображение) хранит изображение в виде массива символов char[][]. Например,

.........
.........
....x....
.........
  1. Создайте конструктор, в котором указывается размер (сколько строк и столбцов) и символ, которым изначально все заполнить. Например, new Drawing(20, 30, ‘.’)
  2. Создайте метод print(), он печатает на экране изображение
  3. Метод setPoint(x, y, char) рисует один символ в изображении. Методу нужно указать, где и какой символ поставить.
  4. Методы drawVerticalLine(...), drawHorizontalLine(...) рисуют вертикальную или горизонтальную линию от заданной точки до заданной. Определите сами параметры для методов, они должны определять расположение линии и символ, которым линию рисовать.
  5. Метод drawRectangle() рисует прямоугольник по двум противоположным углам, стороны прямоугольника вертикальны и горизонтальны. Используйте методы, реализованные ранее.
  6. Необязательно: Рисование произвольной линии (см. необязательную задачу за прошлый семестр)
  7. Рисование круга (или окружности — необязательно). Пользователь указывает центр и радиус, программа должна пройтись по всем точкам поля и, если они лежат в круге, зарисовать их указанным символом.
  8. Реализуйте метод draw(x, y, Drawing d) нарисовать одно изображение на другом. При вызове метода указывается, где и какое изображение рисовать.
  9. (Необязательная задача). Сделайте Drawing неизменяемым: все функции рисования возвращают новый Drawing и не меняют текущий. Чтобы не портить старый класс, назовите новый ImmutableDrawing.
  10. Используйте все реализованные вами ранее методы, чтобы нарисовать Джоконду. Ну или домик.

Рациональные числа

Дедлайн: 10 марта

пакет: ru.spbu.arts.java.oop.rational

Сделаем класс Rational, это рациональные числа, для которых хранится их числитель и знаменатель. С рациональными числами можно совершать арифметические операции.

  1. Создайте класс и добавьте поля n и d (numerator, denominator - числитель, знаменатель). Убедитесь, что поля приватные.
  2. Класс должен иметь конструкторы вида Rational(3, 2) и Rational(3).
  3. Добавьте метод public String toString(), который возвращает естественное представление числа в виде строки, например, 5/7. Если знаменатель равен 1, его не нужно писать. Если числитель ноль, то “0”, а не “0/2” и т.п.
  4. Добавьте метод double toDouble(), который возвращает значение в виде double.
  5. Добавьте сокращение дроби в конструктор: поделить числитель и знаменатель на НОД, убедиться, что знаменатель положительный.
  6. Арифметические функции. add, sub, mul, div:

     Rational r1 = r2.add(r3); // создает новое число
     r2.addInPlace(r3); // добавляет к r2 и изменяет его
    

    Протестируйте! Проверьте, например, что \(\frac16 + \frac13 = \frac12\), и проверьте другие аналогичные равенства для других арифметических операций.

  7. Создайте функцию не в Rational, которая по n считает \(1 + \frac12 + \frac13 + \frac14 + \frac15 + … + \frac1n\) Проверьте, что f(1) = 1, f(2) = 1.5, f(3) = 1.833333333333333
  8. Чему равно f(20)? Почему оно равно тому, чему равно? Как исправить?
  9. Добавьте get- методы для числителя и знаменателя. Чтобы Rational стал совсем неизменяемым, закомментируйте методы *InPlace().
  10. Создайте статические константы ONE и ZERO типа Rational, хранящие элементы 0, 1 в виде рациональных чисел.
  11. Сделайте статическим метод поиска НОД (или сокращения)
  12. Создайте статические версии арифметических операций: static Rational add(Rational r1, Rational r2) и т.п. Эти операции создают новое число, вы можете переиспользовать старые арифметические операции.

Коллекции

Дедлайн: 17 марта

  1. Создайте список из чисел от 1 до $n$, где $n$ в качестве аргумента. Заголовок функции List<Integer> count(int n). Внутри создайте ArrayList.
  2. Сделайте функцию, которая распечатывает длину списка и его значения построчно. Используйте цикл for each
     List<String> list1 = List.of("abc", "xyz", "ooo");
        
     // на экране появится
     //   Элементов в списке: 3
     //   abc
     //   xyz
     //   ooo
     printList(list1);
    
  3. Аналогично предыдущему, но вместе с элементами печатаются их индексы. Здесь можете использовать любой цикл.
     List<String> list2 = List.of("abc", "xyz", "ooo");
        
     // на экране появится
     //   Элементов в списке: 3
     //   1: abc
     //   2: xyz
     //   3: ooo
     printListWithIndices(list1);
    
  4. Сделайте функцию, которая получает два списка и возвращает один, состоящий из двух заданных. Склеивает списки.
     List<String> list3 = List.of("aaa", "bbb", "ccc");
     List<String> list4 = List.of("xxx", "yyy", "zzz");
     List<String> list3plusList4 = concatenateLists(list3, list4);
     System.out.println(list3plusList4); //aaa bbb ccc xxx yyy zzz
     // можно не проверять, что исходные списки не изменились, потому что List.of()
     // создает неизменяемый список. Но можно и проверить:
     System.out.println(list3); //aaa bbb ccc
     System.out.println(list4); //xxx yyy zzz
    
  5. Дан список, верните новый список, в котором все элементы идут в обратном порядке. Сделайте две версии задачи: чистую функцию и функцию, которая меняет заданный список.
     //созадем изменяемый список list5.
     List<String> list5 = new ArrayList<>(List.of("first", "middle", "last"));
     //сначала чистая функция
     List<String> list5rev = reverseList(list5);
     //проверяем, что list5rev перевернутый, а list5 остался без изменений.
     System.out.println("list5rev = " + list5rev + ", but list5 = " + list5);
       
     //теперь функция, которая меняет сам список
     reverseListInPlace(list5);
     //проверяем, что список действительно изменился
     System.out.println("list5 = " + list5);
    
  6. Сделайте по две версии следующих трех функций: чистую функцию и функцию, которая меняет заданный список.
    • Дан List<Integer>, удалите в нем все элементы с четным индексом.
    • Дан List<Integer>, удалить в нем все элементы, которые являются четными числами
    • Дан List<Integer>, удалить в нем все элементы, которые являются четными числами
     List<Integer> ints = List.of(11, 22, 33, 55, 66, 88, 100, 3, 4);
        
     System.out.println("ints = " + ints);
     //функция должна удалить элементы с четными индексами, т.е. оставить только каждое второе число
     System.out.println("ints no even indices = " + filterEvenIndices(ints)); //["22", "55", "88", "3"]
     //функция должна удалить четные числа
     System.out.println("ints without even = " + filterEven(ints)); //["11", "33", "55", "3"]
        
     // В этой части мы проделаем то же самое, но с функциями, которые изменяют переданные списки.
     // слово mutable означает "изменяемый", потому что в этой части мы будем использовать списки,
     // которые можно изменять.
     System.out.println(" ============= mutable lists =================== ");
        
     List<Integer> mutableInts = new ArrayList<>(ints);
     mutableFilterEvenIndices(mutableInts);
     System.out.println("ints no even indices = " + mutableInts); //[22, 55, 88, 3]
        
     mutableInts = new ArrayList<>(ints);
     mutableFilterEven(mutableInts);
     System.out.println("ints without even = " + mutableInts); //[11, 33, 55, 3]
    
  7. Взять текстовый файл, желательно большой на русском. Прочитать из него все слова, каждое слово привести к нижнему регистру и сохранить в множестве HashSet. Вывести все слова.
    • Повторите аналогичные действия для TreeSet, LinkedHashSet. Т.е. Ваша программа должна читать текстовый файл три раза. Убедитесь, что вы не дублируете код, и не скопировали программу три раза для каждого из видов множества. Для этого создайте три разных множества и передавайте их в функцию следующим образом:
     Set<String> hashSet = new HashSet<>();
     Set<String> linkedHashSet = new LinkedHashSet<>();
     Set<String> treeSet = new TreeSet<>();
        
     doReadWordsInFile("a.txt", hashSet);
     doReadWordsInFile("a.txt", linkedHashSet);
     doReadWordsInFile("a.txt", treeSet);
    

Лямбда выражения

Дедлайн: 7 апреля

  1. Создайте интерфейс Printable с одним методом void print(), этот метод означает, что объект печатает что-то на экране.
    1. Сделайте так, что созданный вами ранее класс Drawing реализует этот интерфейс. Проверьте, что получилось.
    2. Создайте класс PrintableLetter, реализующий интерфейс Printable. Пользоваться классом нужно так:

         PrintableLetter pl = new PrintableLetter("x", 10);
         pl.print(); //печатает букву x 10 раз
      
    3. Создайте класс PrintableString, реализующий интерфейс Printable, этот класс хранит строку, пользоваться им нужно так:

         PrintableString ps = new PrintableString("asdf");
         ps.print(); //печатает asdf
      
    4. Создайте массив типа Printable[], заполните его 1) Drawing 2) PrintableLetter 3) PrintableString 4) анонимным классом 5) лямбда выражением. В цикле попросите всех что-нибудь напечатать.

JavaFX

Интерфейс мессенджера

Дедлайн: 14 апреля + 7 = 21 апреля

Интерфейс

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

Интерфейс указан на рисунке. На рисунке заодно указано, какие высоты и ширины должны быть фиксированными, а какие должны тянуться вместе с изменением размеров окна.

  1. Реализуйте интерфейс одним из двух способов. Или обоими, если нет других дел:
    1. Через несколько VBox и HBox внутри друг друга
    2. Через один GridPane
  2. Добавьте несколько фамилий в список контактов.
  3. Нажатие на кнопку переводит введенный текст в поле сообщений, после чего поле ввода очищается. TextArea не должна позволять вводить текст вручную.

Кнопка “не нажимайте эту кнопку” Дедлайн: 14 апреля

  1. Создайте интерфейс из одной кнопки (Button). На кнопке должно быть написано “не нажимайте эту кнопку”.
  2. При нажатии на эту кнопку на кнопке появляется текст “пожалуйста, не нажимайте больше эту кнопку”.
  3. Сделайте так, что следующее нажатие на кнопку снова изменяет текст, но уже на более недовольный. Например, “зачем вы нажали?!”, или “Я просила больше не нажимать.” Еще одно нажатие снова меняет текст на еще более недовольный, и так 3–4 раза. Т.е. каждое нажатие кнопки выбирает очередной текст из нескольких заготовленных
  4. Сделайте так, чтобы после последнего нажатия происходило что-то страшное. Например, с экрана удаляются кнопка, и вместо нее появляется компонент ImageView с картинкой недовольного котика.

Управление кругом

Дедлайн: 21 апреля

Интерфейс

Слева на интерфейсе — панель со Slider, ColorPicker (2 шт). Перед каждым из этих трех элементов есть Label с пояснениями: «радиус», «цвет», «цвет фона». Справа — панель Pane, на ней Circle.

  1. Реализовать интерфейс. Либо через VBox и HBox, либо через GridPane. Желательно, выберите другой вариант, не тот, который вы делали в прошлой задаче. Проследите, что интерфейс тянется на весь размер окна. При этом левая панель с элементами должна иметь фиксированную ширину (предпочитаемую), и высоту на весь размер окна. Всю остальную часть окна занимает Pane с кругом.
  2. Связать свойство радиус круга со свойством value у Slider.
  3. Связать свойство x-координата центра круга с половиной свойства width у Pane.
  4. Связать свойство y-координата центра круга с половиной свойства height у Pane.
  5. Связать свойство цвет для круга со свойством value у первого ColorPicker.
  6. (*) Связать свойство background для Pane так, чтобы оно было того же цвета, что и выбранный пользователем цвет. См., как мы устанавливали цвет панели раньше. Либо через new Background(...), либо через установку стиля.
  7. У слайдера минимальное значение должно быть 0, а вот максимальное значение слайдера надо связать с максимально возможным радиусом круга, пока он не вылезает за Pane. (половина минимума высоты и ширины панели)

Работа с изображениями и цветом

Дедлайн: 12 мая

Ваша задача — создать изображения, наполнить их разноцветными пикселями и отобразить с помощью ImageView. В результате, на окне приложения должны появиться несколько изображений, какие — указано ниже в условиях заданий. Вы можете выложить изображения подряд на FlowPane — это проще всего, или можете сделать GridPane с изображениями и метками Label, содержащими подписи, что на изображении.

  1. Изображение размера 100 на 100, где все пиксели имеют один и тот же, любимый вами цвет. Вы должны в цикле перебрать все координаты пикселей: $x$ от 0 до 99, $y$ от 0 до 99, и каждый пиксель нарисовать этим цветом.
  2. Изображение 256 на 256, где пиксель с координатами $(x, y)$ имеет цвет $R=x$, $G=y$, $B=123$. (вы можете изменить параметры на свой вкус, например, выбрать другое число вместо 123, или переставить местами R, G, B).
  3. Изображение 360 на 100. Пиксель с координатами $(x, y)$ должен иметь цвет в пространстве HSB: $H=x$, $S=y/100$ (потому что насыщенность должна быть в диапазоне от 0 до 1), и $B=0.5$. (Как и в прошлой задаче, вы можете переставить значения).
  4. Рисуем квадрат. Изображение должно быть 100 на 100. Все пиксели с координатами по x или по y от 25 до 75 должны быть черные, остальные — белые.
  5. Рисуем круг. Изображение должно быть 100 на 100. Все пиксели, которые от центра на расстоянии меньше 50 пикселей, должны быть черными. Остальные белыми.
  6. Задача аналогична задаче про HSB, но вы должны использовать пространство LCH (почему LCH: статья на хабр). Оно не встроено в Java, поэтому возьмите этот код для преобразования: LCH.java
  7. Давайте сделаем настояющую радугу. В этот раз изображение должно иметь размер $750 - 380 + 1$ на $100$. По горизонтали длина волны (от 380 до 750), по вертикали все цвета будут одинаковые. Превратить длину волны в цвет RGB можно с помощью такого кода: длина волны в RGB, переведите этот код на Java, там ^ означает возведение в степень.

Фигуры

Нужно развить код из 11 лекции

  1. Добавьте фрактал Мандельброта: дедлайн: 19 мая
  2. Исправьте интерфейс Fractal так, чтобы он возвращал не цвет, а число от 0 до 1. Добавьте интерфейс Palette, который умеет преобразовывать число от 0 до 1 в цвет и создайте несколько реализаций: GrayscalePalette, которая преобразует в оттенки серого. HSBPalette, которая преобразует число от 0 до 1 в цвет с оттенком от 0 до 360, насыщенность и яркость должны быть равны 1. дедлайн: 19 мая
  3. Добавьте на окно кнопки: вверх, вниз, вправо, влево, приблизить, удалить, сохранить (позицию), загрузить (позицию), экспортировать (в PNG или JPEG)
  4. Реализуйте кнопки
  5. Перемещения по фракталу можно реализовать мышью