diff --git a/README.md b/README.md
index d6a4a53..3d110d6 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,11 @@
Disclaimer: лекции по этому курсу в норме читаются голосом. Электронный конспект делается по необходимости из-за карантина. Именно этим объясняется его разрозненность.
- 1. Java: введение, синтаксис -- TODO. [Презентация](http://kspt.icc.spbstu.ru/media/files/2020/java/Java01.pdf)
- 1. Разработка классов с данными и контейнеров (Java, задание 1) -- TODO. [Презентация](http://kspt.icc.spbstu.ru/media/files/2020/java/Java02.pdf)
+ 1. [Java: введение, синтаксис](tutorial/01_Hello_World.adoc). [Презентация](http://kspt.icc.spbstu.ru/media/files/2020/java/Java01.pdf)
+ 1. Разработка классов с данными и контейнеров (Java, задание 1). [Презентация](http://kspt.icc.spbstu.ru/media/files/2020/java/Java02.pdf)
1. [Разработка классов с данными (Kotlin, задание 1.1)](https://github.com/Kotlin-Polytech/KotlinAsFirst/blob/master/tutorial/chapter11.adoc)
1. [Разработка классов-контейнеров (Kotlin, задание 1.2)](https://github.com/Kotlin-Polytech/KotlinAsFirst/blob/master/tutorial/chapter12.adoc)
- 1. Библиотека коллекций Java: итераторы, коллекции, потоки, списки -- TODO. [Презентация](http://kspt.icc.spbstu.ru/media/files/2020/java/Java03.pdf)
+ 1. [Библиотека коллекций Java: итераторы, коллекции, потоки, списки](tutorial/05_Collections_Lists.adoc) -- TODO. [Презентация](http://kspt.icc.spbstu.ru/media/files/2020/java/Java03.pdf)
1. Библиотека коллекций Java: множества, ассоциативные массивы -- TODO. [Презентация](http://kspt.icc.spbstu.ru/media/files/2020/java/Java04.pdf)
1. [Консольные приложения (Java/Kotlin, задание 2): командная строка, обработка исключений](tutorial/07_Console_Exceptions.adoc). Дополнительно: [Презентация лекции](http://kspt.icc.spbstu.ru/media/files/2020/java/Java05.pdf). Внимание: презентация содержит только краткую выжимку, рекомендуется прочитать раздел туториала полностью!
1. [Шаблонные классы (Java/Kotlin)](tutorial/08_Generics.adoc)
diff --git a/pom.xml b/pom.xml
index 8162710..1c850d7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,8 +9,8 @@
0.0.1-SNAPSHOT
- 1.3.11
- 1.8
+ 1.4.21
+ 14
UTF-8
UTF-8
@@ -28,7 +28,7 @@
junit
junit
- 4.12
+ 4.13.1
test
@@ -46,6 +46,11 @@
jdom
1.1
+
+ org.openjfx
+ javafx-controls
+ 15.0.1
+
src
@@ -73,7 +78,7 @@
org.jetbrains.kotlin
${kotlin.version}
- 1.8
+ 14
@@ -92,6 +97,18 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ true
+ part2.recode.java.RecoderLauncher
+
+
+
+
org.apache.maven.plugins
maven-assembly-plugin
@@ -114,6 +131,14 @@
+
+ org.openjfx
+ javafx-maven-plugin
+ 0.0.5
+
+ part3.simple.hello.javafx.HelloJavafx
+
+
\ No newline at end of file
diff --git a/src/part3/simple/components/javafx/Main.kt b/src/part3/simple/components/javafx/Main.kt
index 428b732..8d3abeb 100644
--- a/src/part3/simple/components/javafx/Main.kt
+++ b/src/part3/simple/components/javafx/Main.kt
@@ -13,16 +13,34 @@ class ComponentsView : View("JavaFX components") {
minWidth = 300.0
minHeight = 200.0
center {
- vbox {
- label("Label")
+ hbox {
spacer()
- button("Button").setOnAction {
- alert(Alert.AlertType.INFORMATION, "", "Button pressed!")
+ vbox {
+ spacer()
+ label("Label")
+ spacer()
+ button("Button").setOnAction {
+ alert(Alert.AlertType.INFORMATION, "", "Button pressed!")
+ }
+ spacer()
+ checkbox("Checkbox")
+ spacer()
+ combobox(null, listOf("Combo 1", "Combo 2"))
+ spacer()
}
spacer()
- checkbox("Checkbox")
+ vbox {
+ spacer()
+ circle(radius = 20.0) {
+ fill = c(red = 0.5, green = 0.0, blue = 0.0)
+ }
+ spacer()
+ rectangle(width = 20.0, height = 20.0) {
+ fill = c(red = 0.0, green = 0.0, blue = 0.5)
+ }
+ spacer()
+ }
spacer()
- combobox(null, listOf("Combo 1", "Combo 2"))
}
}
}
diff --git a/test/part2/point/PointTest.java b/test/part2/point/PointTest.java
index 59367c8..27f35d5 100644
--- a/test/part2/point/PointTest.java
+++ b/test/part2/point/PointTest.java
@@ -29,10 +29,10 @@ public void testArrayListMin() {
Point result2 = points2.stream()
.min(Comparator.comparingDouble(Point::abs))
.get();
- System.out.println("Closest point: " + result + " with distance: " + result.abs());
- System.out.println("Time spent: " + (intermediateTime - startTime));
- System.out.println("Closest point: " + result2 + " with distance: " + result2.abs());
- System.out.println("Time spent: " + (Calendar.getInstance().getTimeInMillis() - intermediateTime));
+ System.out.println("[Parallel stream mode] Closest point: " + result + " with distance: " + result.abs());
+ System.out.println("[Parallel stream mode] Time spent: " + (intermediateTime - startTime));
+ System.out.println("[ Simple stream mode] Closest point: " + result2 + " with distance: " + result2.abs());
+ System.out.println("[ Simple stream mode] Time spent: " + (Calendar.getInstance().getTimeInMillis() - intermediateTime));
}
@Test
@@ -48,9 +48,9 @@ public void testArrayListMapFilterCount() {
long result2 = points2.stream()
.map(Point::abs).filter(aDouble -> aDouble > 10.0).count();
- System.out.println("Count of points with abs > 10: " + result);
- System.out.println("Time spent: " + (intermediateTime - startTime));
- System.out.println("Count of points with abs > 10: " + result2);
- System.out.println("Time spent: " + (Calendar.getInstance().getTimeInMillis() - intermediateTime));
+ System.out.println("[Parallel stream mode] Count of points with abs > 10: " + result);
+ System.out.println("[Parallel stream mode] Time spent: " + (intermediateTime - startTime));
+ System.out.println("[ Simple stream mode] Count of points with abs > 10: " + result2);
+ System.out.println("[ Simple stream mode] Time spent: " + (Calendar.getInstance().getTimeInMillis() - intermediateTime));
}
}
\ No newline at end of file
diff --git a/tutorial/01_Hello_World.adoc b/tutorial/01_Hello_World.adoc
new file mode 100644
index 0000000..2c7d2d8
--- /dev/null
+++ b/tutorial/01_Hello_World.adoc
@@ -0,0 +1,416 @@
+= Обзор синтаксиса языка Java
+
+Давайте начнём знакомство с языком Java! Перед вами простейшая программа "Здравствуй, мир".
+
+[source,java]
+----
+// test/Hello.java
+package test;
+public class Hello {
+ public static void main(String[] args) {
+ System.out.println("Здравствуй, мир!);
+ }
+}
+----
+
+Язык Java отличается некоторой многословностью.
+Как видите, даже простая программа на этом языке содержит много всяких слов.
+Попробуем в них разобраться.
+
+== Классы
+
+Класс является главным элементом Java-программы!
+Любая Java-программа всегда содержит хотя бы один класс, а любой код на Java находится внутри какого-то класса.
+
+С помощью классов реализуется *парадигма программирования* ООП = объектно-ориентированное программирование.
+Обучение обычно начинают с процедурного программирования, краеугольным камнем которого
+является разбиение задачи на подзадачи, после чего каждая подзадача реализуется как процедура или функция,
+а задача в целом -- как главная функция.
+В объектно-ориентированном программировании краеугольным камнем являются *понятия* или *объекты*,
+которые фигурируют в задаче.
+Программист, решая задачу, выделяет в ней наиболее важные понятия и реализует их в виде *класса*.
+Класс содержит *данные*, которые описывают понятия задачи, и *функции*, которые работают с этими данными.
+
+В разделе 2 мы подробно рассмотрим определение понятия через класс,
+а пока нам достаточно знать, что в языке Java любой код обязан находиться внутри класса.
+Чтобы определить класс, нужно написать код вида
+[source,java]
+----
+public class Hello {
+ // Тело класса
+}
+----
+
+Ключевое слово _public_ делает класс *открытым*, то есть доступным во всей программе.
+`Hello` является именем класса.
+
+== Пакеты
+
+Язык Java придерживается ряда соглашений о структуре проекта.
+В частности, все файлы с исходным кодом должны находится в директории, которая называется *Source root*
+(переводится на русский примерно как "корневая директория исходного кода").
+В проекте с использованием систем сборки Maven или Gradle директория Source root = `ProjectDir/src/main/java`.
+В проекте без использования систем сборки всё проще: Source root = `ProjectDir/src`.
+Содержимое же директории *Source root* определяется структорой *пакетов* (_package_).
+Например:
+
+[source,java]
+----
+// test/Hello.java
+package test;
+public class Hello {
+}
+----
+
+Обратите внимание на директиву `package test` перед определением класса.
+Это означает, что файл `Hello.java` обязан находиться в директории `test`, которая,
+в свою очередь, должна являться поддиректорией Source root.
+О классе `Hello` в этом случае говорят, что он находится в пакете `test`,
+а *полное его имя* -- test.Hello.
+Имя файла обязано совпадать с именем определённого в нём открытого класса
+(отсюда, в частности, следует, что открытый класс в файле может быть определён лишь один).
+
+[source,java]
+----
+// another/subpack/Some.java
+package another.subpack;
+public class Some {
+}
+----
+
+Здесь класс `Some` находится в пакете `another.subpack`,
+его полное имя -- another.subpack.Some, он обязан находиться в файле `Some.java`,
+а путь к этому файлу должен быть `another/subpack/Some.java`.
+Директория `another` должна быть поддиректорией Source root.
+
+== Тела классов
+
+На языке Java тела классов пишутся в фигурных скобках. В первую очередь, классы состоят из *полей* (данных) и *методов* (операций над данными).
+*Метод* является синонимом *функции* (часто считается, что метод -- это функция, определённая внутри класса).
+В классе `Hello` нет полей, и имеется один метод `main`:
+
+[source,java]
+----
+ public static void main(String[] args) {
+ System.out.println("Здравствуй, мир!);
+ }
+----
+
+Модификатор _public_ задаёт видимость. Как мы уже видели раньше, _public_ -- это открытая видимость, то есть доступная всем.
+
+=== Видимости
+
+Всего в Java имеется четыре разных видимости:
+
+* открытая _public_
+* закрытая _private_ -- может использоваться только внутри класса, подобный член класса виден только внутри этого класса
+* пакетно-закрытая -- не имеет модификатора, видна внутри того же класса, *а также* внутри того же пакета
+* защищённая _protected_ -- также может использовать только внутри класса, видна внутри него же, внутри того же пакета, *а также* внутри наследников этого класса (о них поговорим позже)
+
+Таким образом, видимостей имеется четыре, но модификаторов видимости всего три. Без модификатора (по умолчанию) считается, что видимость пакетно-закрытая (package private). Иногда программисты на Java подобную видимость подчёркивают комментарием:
+[source,java]
+----
+// FILE: SomeClass.java
+package test;
+
+public class SomeClass {
+ private int x = 3;
+ int y = 4;
+
+ public void foo() {
+ System.out.println(x); // Ok (same class)
+ }
+}
+
+/* package-private */ class AnotherClass {
+ public void bar() {
+ SomeClass sc = new SomeClass(); // Ok (public)
+ System.out.println(sc.x); // ERROR! (private, access from another class)
+ System.out.println(sc.y); // Ok (package private, same package)
+ }
+}
+----
+
+Обратите внимание, что имя `SomeClass` обязано совпадать с именем файла (открытый класс), а имя `AnotherClass` -- нет.
+
+Наиболее часто программисты используют закрытую видимость (для описания деталей реализации, которые не должны быть видны снаружи) и открытую видимость (для описания доступных всем действий с объектом). Если вы новичок в Java, неплохое правило на первое время -- делать закрытыми все поля и открытыми все методы (после этого можно закрыть все те методы, которые используются только внутри класса).
+
+Стоит также отметить, что для классов, определённых на верхнем уровне файла, доступными являются только две видимости: открытая _public_ и пакетно-закрытая. Для методов и полей классов, а также для вложенных классов (определённых внутри других классов), доступны все четыре видимости: открытая _public_, закрытая _private_, защищённая _protected_ и пакетно-закрытая.
+
+=== Статичность
+
+[source,java]
+----
+ public static void main(String[] args) {
+ System.out.println("Здравствуй, мир!);
+ }
+----
+
+Как мы видим, функция `main` также является _static_, то есть статической. Чтобы понять, что это такое, нам придётся коснуться разницы между классами (class) и их *экземплярами* (class instance). Иногда вместо "экземпляр класса" говорят "объект класса", это синонимы.
+
+Статические поля и методы являются общими для всего класса. Для обращения к таким полям и для вызова таких методов экземпляр класса не требуется.
+
+Нестатические поля и методы специфичны для экземпляра класса. Для обращения к таким полям и для вызова таких методов у вас должен быть экземпляр класса. Нестатический метод может прочитать нестатическое поле (потому что экземпляр класса у него уже есть) или вызвать нестатический метод -- по той же причине. Статический метод, однако, экземпляра класса не имеет и поэтому не может читать нестатические поля и вызывать нестатические методы без явного указания экземпляра класса.
+
+Попробуйте сами определить, может ли нестатический метод прочитать статическое поле.
+
+_Примерно то же самое можно объяснить и другими словами. Любой *нестатический* метод имеет дополнительный параметр, не указанный явно в списке -- так называемый *получатель* (receiver). Получатель -- всегда ссылка на экземпляр класса, в котором описан данный метод; для её обозначения можно использовать ключевое слово `this`. *Статический* метод такого дополнительного параметра не имеет. Для вызова *нестатического* метода или обращения к *нестатическому* полю всегда требуется получатель правильного типа, указанный явго или неявно. Для вызова *статического* метода или обращения к *статическому* полю этого не требуется._
+
+[source,java]
+----
+public class SomeClass {
+ public int x = 1;
+ static public final y = 2;
+ public void foo() {
+ bar(); // Ok (implicit receiver)
+ this.bar(); // Also Ok (explicit receiver)
+ System.out.println(this.x); // Ok (explicit receiver)
+ System.out.println(y); // Ok (no receiver required)
+ }
+
+ public void bar() {
+ baz(); // Ok (no receiver required)
+ }
+
+ static public void baz() {
+ System.out.println(y); // Ok (no receiver required)
+ System.out.println(x); // ERROR (receiver required!)
+ SomeClass sc = new SomeClass();
+ System.out.println(sc.x); // Ok (explicit receiver)
+ System.out.println("123".x); // ERROR (incorrect explicit receiver)
+ }
+}
+----
+
+=== Типы
+
+Язык Java имеет статическую типизацию. Это значит, что тип любой переменной, параметра, поля, результата функции известен на момент компиляции программы либо выводится во время компиляции программы. Типы бывают разные и делятся на две большие группы:
+
+* *Примитивных* типов всего восемь: четыре целочисленных `int`, `long`, `short`, `byte`; два с плавающей точкой `double` и `float`; логический `boolean`; символьный `char`. Имена примитивных типов записываются со строчной буквы, все они являются ключевыми словами Java (то есть такие же имена нельзя, например, давать переменным). К этой же группе можно условно отнести псевдо-тип `void`, который обозначает отсутствие какого-либо типа. Тип результата функции записывается перед её именем, для функции `main` это как раз `void`, то есть результат у функции `main` отсутствует.
+* *Ссылочных* типов может быть неограниченное количество. Их принципиальное отличие от примитивных состоит в том, что в *стеке* для подобных переменных хранится не значение, а ссылка на участок *кучи*, где уже хранится сам объект. Ссылочные типы могут быть описаны классом, или являться массивом (который в свою очередь может хранить примитивные или ссылочные элементы). В функции `main` тип параметра `args` задан как `String[]` -- обратите внимание, что тип здесь тоже находится перед именем, это общее правило для Java. `String` -- это строковый тип, определяемый библиотечным классом `String`. `String[]` -- это массив строк.
+
+=== Главная функция
+
+По правилам языка Java, исполнение программы начинается с *главной функции*. Подобная функция обязана называться `main`, иметь открытую видимость, быть статической, иметь массив строк в качестве единственного параметра (через него передаются аргументы командной строки, подробнее см. https://github.com/Kotlin-Polytech/FromKotlinToJava/blob/master/tutorial/07_Console_Exceptions.adoc[раздел 7]) и не иметь результата (тип `void`). Разрешается иметь в одной программе несколько главных функций -- в этом случае при работе из IDE мы сами выбираем, с какой из них начинать работу, а при сборке JAR-пакета это указывается в так называемом MANIFEST-файле.
+
+Функция в нашем примере удовлетворяет всем этим требованиям и, значит, является главной. С неё начнётся выполнение нашей маленькой программы.
+
+=== Вывод на консоль
+
+Как можно догадаться из примера, вывод информации на консоль в программе на Java производится с помощью функции `System.out.println()`. Почему у неё такое длинное название? По правилам Java каждая функция обязана находиться в классе; функция `println` находится в классе `PrintStream`, то есть поток печати. Класс `System` содержит ссылки на два стандартных потока печати -- один для вывода обычной информации, статическое поле `out` и другой для вывода ошибок, статическое поле `err`. Запись `System.out` позволяет нам обратиться к статическому полю класса, а дальнейшее `.println` -- вызвать на соответствующем объекте функцию `println`.
+
+== Справочник по синтаксису Java
+
+=== Примитивные типы
+
+* `byte` (1 байт, от -128 до 127)
+* `short` (2 байта, от -32768 до 32767)
+* `int` (4 байта, от -2^31 до 2^31-1)
+* `long` (8 байт, от -2^63 до 2^63-1)
+* `float` (4 байта: 24 бита мантисса + 8 бит порядок)
+* `double` (8 байт: 53 бита мантисса + 11 бит порядок)
+* `boolean` (1 байт: истина или ложь)
+* `char` (2 байта: юникод)
+
+=== Переменные и поля
+
+Как мы уже видели в примерах выше, для описания данных (переменных, параметров, констант, полей) Java использует синтаксис с типом впереди (как в языке Си). Для локальных переменных начиная с версии 10 разрешается используется синтаксис `var <имя> = ...`, позволяющий вывести тип переменной автоматически. Например
+
+[source,java]
+----
+public class SomeClass {
+ public String name = "Some"; // Поле типа String
+ public int x = 2; // Поле типа int
+ public void foo(double param /* параметр типа double */) {
+ char ch = ' '; // Локальная переменная типа char
+ var y = 3 * 5; // Локальная переменная типа double -- используется вывод типов
+ }
+}
+----
+
+=== Константы
+
+Целые
+
+* `57`, `+323`, `-48` (десятичная форма, 4 байта)
+* `024`, `-0634`, `0777` (восьмеричная форма)
+* `0xabcd`, `-0x19f` (шестнадцатеричная форма)
+* `0b010001001` (двоичная форма, только JDK 1.7+)
+* `43_934` (форма с _, только в JDK 1.7+)
+* `1234567890123L`, `0xabcdef1234L` (8-байтные, `long`)
+
+Вещественные
+
+* `37.29`, `-19.41` (обычная форма, 8 байт)
+* `3e+12`, `-1.1e-7` (экспоненциальная форма)
+* `3.6F`, `-1.0e-1F` (4-байтные, `float`)
+
+Символьные
+
+* `'a'`, `'?'`, `' '`, `'\n'`, `'\t'`, `'\\'` (обычный
+вариант)
+* `'\40'`, `'\62'` – символ по восьмеричному коду
+* `'\u0053'` – символ по юникоду
+
+Строковые
+
+* `"Hello, world\n"`
+* `"Сложение " + "строк"`
+
+=== Операции
+
+* Арифметические: `+` `-` `*` `/` `%`. Сложение-вычитание-умножение-деление-взятие остатка.
+* Инкремент/декремент: `++` `--` (увеличение/уменьшение на 1).
+* Логические: `&` `&&` `|` `||` `^` `!`. Все логические операции требуют `boolean` аргументов. `&`, `|`, `^` являются жадными; `&&` и `||` ленивыми.
+* Сравнения: `>` `<` `>=` `<=` `==` `!=`. Сравнение на равенство для примитивных типов происходит по значению, для ссылочных -- по ссылке. Для сравнения объектов по значению существует функция `equals`.
+* Побитовые: `~` `&` `|` `^`. Работают с целочисленными аргументами.
+* Сдвиговые: `<<` `>>` `>>>`. Операция `>>` осуществляет арифметический сдвиг, то есть оставляет знак тем же; операция `>>>` осуществляет беззнаковый сдвиг.
+* Присваивания/модификации: `=` `+=` `-=` `*=` `/=` `%=` `&=` `|=` `^=` `<<=` `>>=` `>>>=`. Пример: `a += b` эквивалентно `a = a + b`.
+* Условная: `a > b ? a : b`. Если условие перед вопросом верно, результат операции -- аргумент перед двоеточием, если нет -- после двоеточия.
+* Приведения типа: `int a = (int)2.5`. "Силой" изменяет тип выражения в правой части. Численные типы при этом приводятся друг к другу (выполняется округление, если это требуется).
+
+=== Ветвления
+
+Основной оператор ветвления `if (condition) { ... } else { ... }`. Условие должно быть логическим. В ветвях может быть любое количество операторов; если ветвь содержит лишь один оператор, фигурные скобки можно опустить (делать этого не рекомендуется). Оператор ветвления не имеет результата, т.е. код вида `int x = if (a > b) a else b` запрещён, вместо этого можно применять условную операцию `int x = a > b ? a : b`.
+
+Табличное ветвление по ключу
+
+[source,java]
+----
+switch (someInt /* ключ */) {
+case 1:
+ ...
+ break;
+case 5:
+ ...
+ break;
+default:
+ ...
+ break;
+}
+----
+
+работает так. Если `someInt` в примере равно 1, код выполняется начиная с метки `case 1`. При выполнении оператора `break` мы покидаем конструкцию `switch`. Аналогично, если `someInt` равно 5, выполняем код начиная с метки `case 5`. Если ни одна из меток не содержит истинного значения -- выполняем код с метки `default`.
+
+По правилам Java, ключом оператора `switch` может являться
+
+* целое число
+* символ
+* элемент перечисления
+* строка (начиная с версии 1.7)
+
+Начиная с версии 14, Java разрешает использование *switch expressions* (выражений табличного ветвления), то есть оператор `switch` теперь может иметь результат (который может быть присвоен переменной или использован каким-либо иным образом). Например:
+
+[source,java]
+----
+int grade = switch (gradeWord /* ключ */) {
+case "уд", "удовл", "удовлетворительно":
+ yield 3;
+case "хор", "хорошо":
+ yield 4;
+case "отл", "отлично":
+ yield 5;
+case "неуд", "неудовл", "неудовлетворительно":
+ yield 2;
+default:
+ yield 0;
+}
+----
+
+Выполнение команды `yield` здесь ведёт к формированию результата `switch` и немедленному выходу из конструкции. Можно считать, что `yield` ~= *return from switch*.
+
+Тот же код может быть записан без помощи `yield`, если использовать новый синтаксис `switch` с заменой `:` на `->`:
+
+[source,java]
+----
+int grade = switch (gradeWord /* ключ */) {
+ case "уд", "удовл", "удовлетворительно" -> 3
+ case "хор", "хорошо" -> 4
+ case "отл", "отлично" -> 5
+ case "неуд", "неудовл", "неудовлетворительно" -> 2
+ default -> 0;
+}
+----
+
+=== Циклы
+
+Язык Java включает четыре вида циклов: while (с предусловием), do-while (с постусловием), for (со счётчиком), for[each] (для каждого).
+
+[source,java]
+----
+public class SomeClass {
+ public void foo() {
+ // 1. Проверить условие, если оно верно, выполнить тело, если нет, выйти из цикла
+ // 2. Вернуться к пункту 1
+ while (condition) {
+ doSomething(); // Тело
+ }
+
+ // 1. Выполнить тело.
+ // 2. Проверить условие, если оно верно, вернуться к пункту 1, если нет, выйти из цикла
+ do {
+ doSomething(); // Тело
+ } while (condition);
+
+ // 1. Выполнить начало (i=0)
+ // 2. Проверить условие (i<10), если оно верно, выполнить тело, если нет, выйти из цикла
+ // 3. Выполнить шаг
+ // 4. Вернуться к пункту 2
+ for (int i=0 /* начало */; i<10 /* условие */; i++ /* шаг */) {
+ doSomething(); // Тело
+ }
+
+ int[] arr = new int[] { 2, 3, 5, 8, 13 }; // Создать массив из пяти элементов
+ // 1-5. Для каждого из пяти элементов массива вызвать doSomething()
+ for (int element: arr) {
+ doSomething(); // Тело
+ }
+ }
+}
+----
+
+Цикл на Java немедленно прерывается, если в его теле выполняется оператор `break`. Другой оператор управления `continue` заставляет цикл немедленно перейти к проверке условия выполнения следующей итерации (другими словами, `continue` немедленно прерывает текущую итерацию цикла). Напомним, что итерацией цикла называется одно выполнение его тела.
+
+=== Строки
+
+Строки в Java описываются библиотечным классом `String`. Строковые константы записываются в двойных кавычках. По правилам Java строки можно складывать с помощью оператора `+` и сравнивать на равенство с помощью метода `equals`. Использовать для строк оператор ссылочного равенства `==` не рекомендуется.
+
+[source,java]
+----
+public class SomeClass {
+ public void foo(String s1) {
+ String s2 = "Alpha";
+ System.out.println(s1.equals(s2)); // true, если s1 тоже "Alpha"
+ System.out.println(s1 == s2); // true, если s1 и s2 являются ссылками на один и тот же объект класса String
+ String s3 = "Alpha"; // По факту s2 и s3 ссылаются на один и тот же объект
+ System.out.println(s2 == s3); // true
+ String s4 = "Al" + "pha";
+ System.out.println(s3 == s4); // Все ещё true
+ }
+
+ public void bar() {
+ foo("Alpha"); // Выведется четыре раза true
+ }
+}
+----
+
+Компилятор Java умеет оптимизировать строковые константы. Если, например, у вас в программе 20 раз встречается константа `"Alpha"`, в памяти будет создан всего один строковый объект с таким содержимым. И даже если вы напишите что-то вроде `String s = "Al" + "pha";`, ваша строка всё ещё равняется `"Alpha"` и переиспользует тот же самый объект. Если, однако, строка составляется из нескольких других и компилятор не может определить, что их значения всегда одинаковы, объекты будут созданы заново. Попробуйте в качестве упражнения написать пример, в котором ссылочное равенство даёт результат `false`, несмотря на то, что строки равны по значению.
+
+=== Массивы
+
+Массивы в Java -- единственный составной тип, существующий на уровне языка и не имеющий библиотечного описания (скажем, связанные списки `LinkedList` и строки `String` описаны в Java как классы стандартной библиотеки). Любой массив -- ссылочный тип. Массив элементов типа `Type` обозначается как `Type[]`. Размер (длина) массива всегда задаётся при его создании и в дальнейшем не меняется. Индексы элементов начинаются от нуля, индекс последнего элемента равен размеру массива минус 1. Примеры:
+
+[source,java]
+----
+public class SomeClass {
+ public void foo() {
+ double[] darr = new double[10]; // Создать массив из 10 вещественных элементов, содержащий нули
+ String[] sarr = new String[] { "Alpha", "Beta", "Omega" }; // Создать массив из трёх заданных строк
+ int[] arr = null; // Создать нулевую ссылку на массив
+ System.out.println(sarr[1]); // "Beta". Индексация идёт с нуля
+ System.out.println(darr[10]); // Ошибка во время выполнения -- ArrayIndexOutOfBoundsException. Допустимы индексы от 0 до 9
+ System.out.println(arr[0]); // Ошибка во время выполнения -- NullPointerException. Попытка обратиться к массиву по нулевой ссылке
+ System.out.println(darr.length); // 10 -- число элементов (размер, длина) массива
+ }
+}
+----
diff --git a/tutorial/05_Collections_Lists.adoc b/tutorial/05_Collections_Lists.adoc
new file mode 100644
index 0000000..f015dd0
--- /dev/null
+++ b/tutorial/05_Collections_Lists.adoc
@@ -0,0 +1,146 @@
+= Коллекции, итераторы, списки
+
+Здесь мы начинаем рассматривать библиотеку коллекций Java.
+
+== Основные понятия
+
+Библиотека коллекций строится на трёх видах кирпичей:
+
+* *интерфейсы* `interface` определяют, что именно умеет делать тот или иной объект, но обычно ничего не говорят о том, как он это делает
+* *абстрактные классы* `abstract class` содержат так называемую частичную реализацию объекта; в библиотеке коллекций они, как правило, реализуют все необходимые методы через несколько базовых
+* *классы* `class` отвечают на вопрос о том, какие именно данные хранит объект и как именно он осуществляет те или иные операции
+
+В прикладном коде интерфейсы обычно используются для определения типов переменных, параметров, полей, результатов методов; классы -- для создания объектов при вызове их конструкторов. Абстрактные классы в прикладном коде используются сравнительно редко, они позволяют за короткое время написать свою реализацию коллекции, если это потребовалось.
+
+Для описания отношений вида *подтип*, *подвид* используется концепция *наследования*. В объектно-ориентированном программировании ситуация, когда класс (интерфейс) Б является *наследником* класса (интерфейса) А, означает, что:
+
+* по смыслу объект типа Б является разновидностью объекта типа А
+* объект типа Б можно употреблять везде, где нужен тип А
+* объект типа Б содержит все данные, которые есть в объекте типа А (и, возможно, какие-либо другие)
+* на объекте типа Б можно вызывать все методы, которые можно вызывать на объекте типа А (и, возможно, какие-либо другие)
+
+Сокращённо говорят, что "public inheritance means IS A", или "открытое наследование означает ЭТО ЕСТЬ". В Java любое наследование считается открытым (в отличие от, например, {cpp}). Кроме этого, с наследованием связано ещё два специальных слова:
+
+* про класс Б можно сказать, что он *расширяет* класс А или *реализует* интерфейс А (в обоих случаях он является наследником А)
+* про интерфейс Б можно сказать, что он *расширяет* интерфейс А (и опять-таки это синоним слова "наследник")
+* интерфейс Б никогда не может являться наследником класса А (запрещено правилами Java).
+
+== Иерархия коллекций
+
+Коллекции Java образуют иерархию наследования. С ней можно ознакомиться, например, здесь: https://en.wikipedia.org/wiki/Java_collections_framework. Подобное изображение иерархий распространено среди программистов; в них наследник всегда рисуется ниже своего базового класса (интерфейса) и соединяется с ним стрелкой, ведущей в сторону базового класса (интерфейса).
+
+Вершиной иерархии коллекций, а следовательно, самым общим типом, является интерфейс `Iterable`. Его свойства наследуют все коллекции библиотеки. Благодаря этому интерфейсу нам доступен в Java код следующего вида
+
+[source,java]
+----
+ public static void foo(Iterable container) {
+ for (String element: container) {
+ bar(element);
+ }
+ }
+----
+
+На месте `Iterable` здесь может стоять `Collection`, `List`, `Set`, `Queue` и цикл for-each всё равно будет для них доступен. Интерфейс `Iterable` тесно связан с интерфейсом-помощником `Iterator` (так говорят в ситуации, когда помощник является бессмысленным без основного интерфейса, и наоборот). Его формальное описание выглядит так:
+
+[source,java]
+----
+public interface Iterable {
+ Iterator iterator(); // то есть Iterable даёт возможность получить Iterator
+}
+----
+
+`Iterator` является чем-то вроде указателя, путешествующего по коллекции. В момент создания он "смотрит" перед нулевым элементом коллекции. Итератор содержит три метода:
+
+[source,java]
+----
+public interface Iterator {
+ boolean hasNext(); // Возвращает true, если за итератором есть ещё элементы
+ E next(); // Возвращает следующий элемент за итератором И передвигает итератор за него
+ void remove(); // Удаляет из коллекции элемент, который вернул последний вызов next
+}
+----
+
+Порядок ручного использования итератора в общем случае выглядит так:
+
+1. Создать итератор с помощью вызова `iterator()`.
+2. Проверить, есть ли следующий элемент `hasNext()`. Если его нет -- перебор закончен.
+3. Достать следующий элемент `next()`.
+4. Сделать необходимую его обработку. Если для данного элемента это требуется, его можно удалить вызовом метода `remove()`.
+5. Вернуться к пункту 2.
+
+Всё то же самое можно выполнить с помощью обычного цикла `for-each`, кроме удаления элемента.
+
+[source,java]
+----
+ public static void foo(Iterable container) {
+ for (String element: container) {
+ bar(element);
+ }
+ // Is equivalent to
+ Iterator it = container.iterator();
+ while (it.hasNext()) {
+ String element = it.next();
+ bar(element);
+ }
+ }
+----
+
+Важно, однако, отметить, что удалить элемент в цикле for-each невозможно. Например:
+
+[source,java]
+----
+public class SomeClass {
+ private static boolean condition(String s) {
+ return ...// Some condition
+ }
+
+ public static void foo(Collection container) {
+ Iterator it = container.iterator();
+ while (it.hasNext()) {
+ String element = it.next();
+ if (condition(element)) it.remove();
+ }
+ // Is NOT equivalent to
+ for (String element: container) {
+ if (condition(element)) {
+ container.remove(element); // Produces ConcurrentModificationException
+ }
+ }
+ // However, it's possible...
+ container.removeIf(SomeClass::condition);
+ }
+}
+----
+
+Что такое `ConcurrentModificationException`? Это исключение, связанное с так называемым контрактом итератора. Согласно этому контракту, во время работы итератора (от момента, когда он был создан, и до момента, когда на нём был вызван последний метод), запрещается менять коллекцию любым способом, за исключением вызова `remove` на итераторе.
+
+Про метод `removeIf` см. раздел "Потоки и функции высшего порядка".
+
+=== Интерфейс Collection и его реализации
+
+По смыслу интерфейс `Collection` описывает объект, содержащий некоторое количество однотипных объектов. По контракту коллекции, туда можно добавлять элементы и удалять их, а также перебирать их с помощью итератора. Коллекция "как есть" (т.е. без расширений) не нумерует свои элементы, и не запрещает добавлять в коллекцию равные элементы.
+
+Содержимое интерфейса описано здесь: https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html. Повторять это описание вряд ли имеет смысл; здесь мы лишь подчеркнём, что отдельного внимания заслуживают методы `stream()`, `parallelStream()`, `spliterator()` и `removeIf(predicate)`. Все они относятся к поддержке в Java функций высшего порядка, появившейся в версии 1.8 языка. Про неё см. раздел "Потоки и функции высшего порядка".
+
+Коллекция "как есть" не имеет полных реализаций. Существует, однако, абстрактный класс `AbstractCollection`, обеспечивающий так называемый "скелет" реализации. Расширив этот абстрактный класс с помощью `extends`, мы можем создать собственную реализацию коллекции, добавив туда нужные для хранения данных поля и всего три метода:
+
+* `iterator()` и `size()`, если мы хотим создать неизменяемую коллекцию. Итератор при этом должен поддерживать `next()` и `hasNext()`.
+* `add()`, если мы хотим создать изменяемую коллекцию. Следует добавить также реализацию `remove()` в итераторе.
+
+Некоторые методы -- в первую очередь `clear()` -- абстрактный класс `AbstractCollection` реализует заведомо неэффективно.
+
+=== Списки: интерфейс List и его реализации
+
+Список `List` расширяет коллекцию `Collection`. В отличие от коллекции "вообще", список является пронумерованным -- у каждого элемента есть свой номер (индекс), а по индексу можно достать или изменить элемент `get`, `set`. Индексы нумеруются от нуля до числа элементов `size()` минус один. Список, по-видимому, самая используемая структура данных в языках программирования.
+
+Также список добавляет ряд методов, связанных с работой с индексами -- например, вставка в список элемента по заданному индексу `add(index, element)` (данная операция "раздвигает" список в этом месте и вставляет туда новый элемент), или, наоборот, удаление по заданному индексу `remove(index)` (здесь список наоборот "схлопывается" в данном месте). Полный список методов можно посмотреть здесь: https://docs.oracle.com/javase/8/docs/api/java/util/List.html. У некоторых методов меняются контракты:
+
+* Метод `add(element)` в списке всегда вставляет элемент именно в его конец
+* Итератор `iterator()` перебирает список по возрастанию индексов
+* Сравнение на равенство `equals()` возвращает `true`, если размеры списков равны, и равны все пары элементов с одинаковыми индексами. При сравнении списка с не-списком всегда возвращается `false`.
+
+К списку функций высшего порядка добавляются `replaceAll(unaryOperator)` и `sort(comparator)`.
+
+=== Потоки и функции высшего порядка ===
+
+TODO
diff --git a/tutorial/10_JavaFX.adoc b/tutorial/10_JavaFX.adoc
index b006f03..2de237b 100644
--- a/tutorial/10_JavaFX.adoc
+++ b/tutorial/10_JavaFX.adoc
@@ -6,7 +6,7 @@
== Поддержка на Java и Kotlin
-Библиотека JavaFX входит в состав JDK 1.8, 9 и 10, поэтому для этих JDK программы на Java и Kotlin могут, в принципе, использовать её без подключения дополнительных зависимостей. При использовании JDK 11 и более поздней необходимо явное подключение JavaFX в виде https://gluonhq.com/products/javafx/[загруженного пакета], https://openjfx.io/openjfx-docs/#maven[Maven-зависимости] или https://openjfx.io/openjfx-docs/#gradle[Gradle-зависимости]. Данный проект пока использует JDK 1.8, поэтому JavaFX-зависимость доступна ему непосредственно; загрузить JDK 1.8 можно https://www.oracle.com/java/technologies/javase-jdk8-downloads.html[отсюда].
+Библиотека JavaFX входит в состав JDK 1.8, 9 и 10, поэтому для этих JDK программы на Java и Kotlin могут, в принципе, использовать её без подключения дополнительных зависимостей; загрузить JDK 1.8 можно https://www.oracle.com/java/technologies/javase-jdk8-downloads.html[отсюда]. При использовании JDK 11 и более поздней необходимо явное подключение JavaFX в виде https://gluonhq.com/products/javafx/[загруженного пакета], https://openjfx.io/openjfx-docs/#maven[Maven-зависимости] или https://openjfx.io/openjfx-docs/#gradle[Gradle-зависимости]. Данный проект использует JDK 14 и использует библиотеку javafx версии 15.0.1 (см. файл pom.xml).
Для программ на Kotlin существует удобный DSL (Domain Specific Language = предметно-ориентированный язык) https://tornadofx.io[tornadofx] -- см. также https://github.com/edvin/tornadofx[его GitHub-репозиторий]. Этот DSL может быть подключен к программам на Kotlin как дополнительная библиотека -- подробности о подключении дополнительных библиотек см. в https://github.com/Kotlin-Polytech/FromKotlinToJava/tree/master/tutorial/07_Console_Exceptions.adoc[7-м разделе]; опять-таки можно использовать Maven- или Gradle-зависомость, примеры есть https://github.com/edvin/tornadofx[на GitHub в README].