Pulsate - On
Waves - On

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

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

Классы и Объекты: Что такое класс и объект? Как они взаимосвязаны?
Класс — это шаблон или чертеж для создания объектов. Он описывает структуру и поведение, которыми должны обладать создаваемые на его основе объекты. В классе определяются поля (переменные) и методы (функции).
Объект — это конкретный экземпляр класса, созданный в памяти. Он обладает состоянием (значениями полей) и поведением (методами), заданным в классе. Каждый объект может иметь свои уникальные значения переменных, определённых в классе.
Взаимосвязь: Класс — это как чертеж или модель, а объект — это его реализация.

4 Принципа в ООП

1. АБСТРАКЦИЯ
Абстракция позволяет выделить только важные характеристики объектов и скрыть детали их реализации. В Java абстракция достигается с помощью абстрактных классов и интерфейсов, которые определяют шаблоны методов, чтобы их можно было реализовать в других классах.
Различия между абстрактным классом и интерфейсом:
• Абстрактный класс может наследовать только один другой класс, но реализовывать несколько интерфейсов, тогда как интерфейс может наследовать только другие интерфейсы.
• Абстрактные классы могут иметь конструкторы, интерфейсы — нет.
• В абстрактном классе переменные могут иметь модификаторы доступа `public`, `protected`, или `private`, тогда как в интерфейсе они по умолчанию public static final.
• В интерфейсах можно реализовать default и static методы, которые имеют готовую реализацию.
• Классы наследуют абстрактные классы с помощью `extends`, интерфейсы — с помощью `implements`.

2. ИНКАПСУЛЯЦИЯ
Инкапсуляция — это принцип, который позволяет скрыть детали реализации и защищать данные внутри класса. Для этого в Java используются модификаторы доступа (`private`, `default`, `protected`, `public`):
Private — переменные и методы видны только в рамках текущего класса.
Default — доступ предоставляется в пределах одного пакета.
Protected — доступ доступен в подклассах и в пределах одного пакета.
Public — доступ открыт для всех.

3. НАСЛЕДОВАНИЕ
Наследование позволяет создавать новые классы на основе уже существующих. Новый класс (подкласс) получает свойства и методы родительского класса, что облегчает код и позволяет повторно использовать уже написанный функционал.
Пример: Класс `Телефон` может быть родительским для классов `СотовыйТелефон` и `Смартфон`. Эти классы-наследники могут добавлять уникальные для них функции.

4. ПОЛИМОРФИЗМ
Полиморфизм — это возможность объектов выполнять одинаковые действия по-разному. Полиморфизм в Java бывает двух видов:
Статический полиморфизм (Overloading): реализуется во время компиляции и позволяет использовать несколько методов с одинаковым именем, но разными параметрами.
Динамический полиморфизм (Overriding): реализуется во время выполнения, когда метод, определённый в суперклассе, переопределяется в подклассе.

Два способа реализации полиморфизма:
1. Статический полиморфизм (Overloading) — например, метод `Assert.assertTrue` может быть вызван с разными параметрами. Такой метод можно перегрузить, создавая несколько вариантов одного метода с различными параметрами.
2. Динамический полиморфизм (Overriding) — например, если создать метод для умножения чисел в родительском классе, его можно переопределить в дочерних классах, чтобы использовать разные логики умножения или передавать разные значения в зависимости от класса.

Пример: Наследование, Инкапсуляция и Полиморфизм в одном примере.

Иерархия коллекций в JAVA

Иерархия коллекций состоит из интерфейсов и классов. Это способ хранения и обработки данных. Это что-то вроде массивов, только больше места для значений. С ее помощью классы коллекций реализуют различные алгоритмы и структуры данных, например, стек, очередь, дерево и т.д. В JAVA несколько коллекций, у каждого есть свой интерфейс, который определяет свой функционал. Среди интерфейсов есть следующие:
Существуют две коллекции COLLECTION interface and MAP interface.

COLLECTION наследует интерфейсы: LIST, QUEUE, SET.
LIST (обьекты в виде списка) реализуется в классах:
ArrayList (пример) – список массивов, элементы пронумерованы(у каждого свой индекс). Методы - add, set(менять значение), remove, clear. Стандартная длина ячеек - 10.
LinkedList (пример) – это двусвязный список, к примеру, 2ой элемент посылается на 1 и на 3, все элементы взаимодействуют между собой.
SET - все обьекты уникальны, не повторяются, элементы можно быстро найти по хешу, реализуется в классах:
HashSet (пример) – каждый объект идентифицируется по хешу, добавляются в хаотичном порядке, если объекты имеют одинаковый хешкод, сделав это самостоятельно, то это будет коллизия!
LinkedHashSet (пример) – элементы добавляются в том порядке, в котором их добавляют, взаимодействуют между собой.
TreeSet (пример) – элементы сортируются в алфавитном порядке, либо от 1 и по порядку, автоматом удаляются уникальные элементы.
QUEUE (пример) – данные в виде очереди: первый зашел, первый вышел, наследует:
Deque (пример) – данные в виде очереди: первый зашел и последний вышел.

MAP INTERFACE (набор элементов, которые сортируются по ключ-значению) реализуется в классах:
HashMap (пример) – этот класс использует хеш-таблицу для хранения данных.
LinkedHashMap (пример) – сохраняет порядок вставки элементов.
TreeMap (пример) – элементы идут по алфавитному порядку, либо от 1 и по порядку.
Ключи одинаковыми быть не могут, все элементы лежат в массиве.

Иерархия исключений в Java

Иерархия исключений начинается с Throwable, который является базовым классом для всех исключений. Из него наследуются два основных типа: Exception и Error.

Exception делятся на два типа: проверяемые (checked) и непроверяемые (unchecked). Проверяемые исключения — это те, которые необходимо обрабатывать с помощью блоков try-catch-finally, например, IOException, FileNotFoundException, и SocketException.

Непроверяемые исключения — это ошибки, возникающие во время выполнения программы, которые не требуют обязательной обработки, такие как RuntimeException, IndexOutOfBoundsException, и ArrayIndexOutOfBoundsException.

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

Try-Catch-Finally

Это конструкция для обработки исключений (exceptions).
Try — в этом блоке размещается код, который может вызвать исключение. Здесь программа «пытается» выполнить код и перехватить возможную ошибку. Блок try должен быть один.
Catch — если происходит исключение, этот блок его «ловит» и обрабатывает. Можно использовать несколько блоков catch, чтобы обрабатывать разные типы исключений.
Finally — действие должно быть выполнено. Он используется для освобождения ресурсов, таких как файлы или соединения с базой данных. Этот блок необязателен и может быть только один.
После обработки исключения с помощью этой конструкции программа продолжает выполнение.
Пример Try-Catch

Throw, throws

Это конструкции для обработки exception.
THROW - помечается в методе. Создает генерацию exception. Если он есть, throw его выбрасывает.
Пример throw

THROWS - добавляется в сигнатуру метода, указывая, что этот метод может выбросить нам exception.
Пример throws

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

Паттерны программирования

Это то, как должен быть написанный код.
Это готовое к использованию решение, которое можно добавить к себе в проект, но его еще надо подстроить. Это готовый принцип какой-то работы, которым можно воспользоваться. Например, есть велосипед, чтоб создать что-то похожее, у нас уже есть принцип как это выглядит.
PageObject - паттерн для создания тестового фреймворка.
Разбивание на Пейджи, на локаторы и на компоненты (компоненты - это, например, какой-то Pop-up, всплывающие окно, развертывание меню).
Пример: PageObject - тестовый фреймворк, проект на Github.
PageFactory - часть реализации PageObject. Делает код более структурированым. Задаем переменную и если упадет тест - увидим в какой переменной была ошибка. Также сокращает написание кода.

А для использования паттерны бывают:
Билдер - с его помощью мы можем задать параметры в запросы (when,then,body - Rest-Assured), тем самым сделав код читабельней.
Фектори - создание обьекта, без указания класса, особо не используется.
Синглтон - создание лишь одного экземпляра для прогона большего количества теста, к примеру, "корзина".
Итератор - позволяет клиенту обходить разные коллекции, легко будет пользоваться сайтом.

Циклы (loops)

Циклы – это специальная конструкция, за счет которой наше действие может выполнится несколько раз.
В Java есть такие циклы, как: for, while, do-while, forEach, break-continue
For – выполняется заданное количество раз, он более гибкий (можно прогнать тест задом наперед, к примеру), while – продолжает выполнятся, пока условие истинное, do-while – выполняет блок кода хотя бы 1 раз и до того момента пока условие работает, forEach - перебор элементов.
Видео-пример: цикл For.

Object class

В Java, "Object" — это корневой класс для всех объектов. Все классы в Java реализуются с помощью Наследования.
Существуют такие важные методы, как:
1. equals(Object obj): Этот метод позволяет сравнивать объекты на равенство. Обычно он переопределяется в ваших собственных классах, чтобы обеспечить правильное сравнение. А == - сравнивает посылания обьектов.
2. hashCode(): Этот метод сравнивает хэш-код (индивидуальный номер) объектов. Хеши у всех обьектов разные, но могут быть одинаковыми, если сделать это самостоятельно.
3. toString(): Метод возвращает строковое представление объекта. Обычно он переопределяется, чтобы предоставить информативное описание объекта.
4. getClass(): Этот метод возвращает класс объекта в виде экземпляра класса Class. Например, myObject.getClass() вернет информацию о классе, к которому принадлежит myObject.
5. wait(), notify(), notifyAll(): Эти методы используются для синхронизации потоков в многозадачных приложениях.
6. finalize(): Этот метод вызывается перед тем, как объект будет утилизирован сборщиком мусора.
7. clone

Static Keyword

Когда член класса объявляется как static, он связывается с классом, а не с экземпляром этого класса.
Static метод - можем метод использовать без создания объекта в этом же классе (через название класса).
Static переменная - используется без создания экземпляра в классе (также через название класса).

Final Keyword

Обозначая метод или переменную модификатором final, мы имеем ввиду что класс - не сможем наследовать, а метод - не сможем Override'ить.

Super Keyword

Есть два ключевых слова this и super.
This посылается на объект данного класса, а super посылается на объект суперкласса, то есть, тот класс откуда я наследуюсь, то есть, от родительского класса.

Final, Finally, Finalize

Final - ключевое слово в Джаве, в методе - не можем Override'ить, в классе - не можем наследовать.
Finally - один из блоков Try Catch, где обрабатывается исключение. Это блок, где действие должно выполнится. Он не обязателен. (ArrayList - не может быть final)
Finalize - deprecated метод, старый, особо не используется, раньше работал, если не ошибаюсь, для очищения сборки, сейчас не юзается.

Variables | Переменные

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

Классы | Объекты

Класс - шаблон для сохранения объектов.
Объект - экземпляр класса.
Класс содержит - конструкторы, методы, переменные, блоки. Без конструктора нельзя создать объект. Конструкторы бывают: дефолтные и пользовательские(явные).

String | StringBuilder | StringBuffer

Класс String предназначен для работы со строками, то есть это массив char'ов.
Класс StringBuilder - является не потокобезопасным, его методы не синхронизированы, но он будет более эффективным.
Класс StringBuffer - является потокобезопасным. Его методы синхронизированы, что делает его безопасным для использования в многопоточных приложениях, но он будет медленне, чем StringBuilder.

Types of Variables: Local, Instance, and Static - Типы переменных

Локальные переменные (local variables) — это переменные, которые объявляются внутри метода, конструктора или блока. Они доступны только в пределах этого метода или блока и не сохраняют свое значение после завершения работы метода.

Переменные экземпляра (instance variables) — это переменные, которые объявляются в классе, но за пределами методов, конструкторов или блоков. Они принадлежат конкретному объекту (экземпляру) класса. Каждое создание нового объекта получает свою копию этих переменных. Значения переменных экземпляра могут быть разными для каждого объекта.

Статические переменные (static variables) — это переменные, которые объявляются с ключевым словом static в классе, но вне методов, конструкторов или блоков. Они принадлежат самому классу, а не его экземплярам. Все экземпляры класса разделяют одну копию статической переменной, которая инициализируется только один раз при загрузке класса в память. Вызывать статические переменные можно через имя класса, без создания объекта.

Variable Declaration and Initialization | Объявление и инициализация переменной

Переменная считается инициализированной, когда ей присвоено значение. Без значения она просто объявлена, но не готова к использованию.

Объявление переменной включает указание её типа и имени:
int number;
После этого можно присвоить ей значение (инициализировать):
number = 10;

Или сразу инициализировать при объявлении:
int number = 10;

Это позволяет программе работать с переменной и использовать её значение.

Data Types | Типы данных

Примитивные данные: byte, short, int, long, зависит от размерности и количества чисел.
Типы данных с плавающей точкой: float, double. В случае в float в конце значения обязательно поставить букву “f”. Пример: float num = 23.3f;
Типы данных с символами: char – указывается в одинарных скобках. Пример: char ch1 = ‘abc’; char ch2 = ‘/123/’;
Типы данных логическое значение: Boolean – передает значение true or false
Типы данных со строкой: String – передает строчные значение в “”. Пример: String name = “Hello World”

Generics | Дженерики

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

Дженерики позволяют избежать ошибок времени выполнения, так как проверка типов происходит на этапе компиляции.

Пример использования дженериков можно увидеть при работе с коллекциями. Вместо того, чтобы хранить объекты разных типов, можно задать определённый тип:
List<String> list = new ArrayList<>();

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

Распределение памяти для примитивных и непримитивных типов данных

В Java память делится на две основные области: Стек (Stack) и Куча (Heap).

Примитивные типы данных (int, double, char и т.д.) хранятся в стеке. Стек управляет вызовами методов и хранит локальные переменные. Он работает по принципу LIFO (Last In, First Out), что делает доступ к данным очень быстрым.

Объекты и непримитивные типы данных (например, классы, массивы, интерфейсы) хранятся в куче. Куча используется для динамического распределения памяти. Когда создается объект, для него выделяется память в куче.

String Pool — это специальная область в куче, где хранятся строки. Если вы создаете строку без оператора new, она помещается в String Pool. Например:
String str1 = "Hello";
В этом случае строка "Hello" будет храниться в String Pool.

Однако если вы создаете строку с помощью new, как в:
String str2 = new String("Hello");
то новый объект строки будет создан в куче, даже если строка "Hello" уже есть в String Pool.

Operators | Операторы

В Java существует множество операторов, которые позволяют выполнять различные действия в программе. Например:
1. Оператор присваивания: Assignment operators • =: Присваивает значение правой части выражения левой части (например, int x = 10;).
2. Операторы арифметических операций: Arithmetic operators • +: Сложение • -: Вычитание • *: Умножение • /: Деление • %: Остаток от деления
3. Операторы сравнения: Comparison operators • ==: Равенство • !=: Неравенство • <: Меньше • >: Больше • <=: Меньше или равно • >=: Больше или равно
4. Операторы логических операций: Logical operators • &&: Логическое "и" (и) • ||: Логическое "или" (или) • !: Логическое "не" (отрицание)
5. Операторы инкремента и декремента: • ++: Инкремент (увеличение значения на 1) • --: Декремент (уменьшение значения на 1)
6. Операторы условного выражения: • if: Условный оператор • else: Ветвь "иначе" для условного оператора • else if: Дополнительные условные ветви • switch: Множественный выбор
7. Операторы цикла: • for: Цикл с предусловием • while: Цикл с постусловием • do-while: Цикл с проверкой условия после выполнения тела цикла
8. Операторы выбора: • switch: Множественный выбор
9. Операторы ввода-вывода: • System.out.println(): Вывод на консоль • System.in: Ввод с клавиатуры
10. Операторы приведения типов: • (тип) выражение: Приведение типа (например, (int) 3.14 приведет 3.14 к целочисленному типу).
11. Операторы экземпляра: • .: Используется для доступа к членам объекта (например, obj.method()).
12. Оператор условного тернарного выражения: • ?:: Позволяет создать краткое условное выражение (например, int result = (a > b) ? a : b;).

If - else | Switch - case

Это конструкции для проверки нашего объекта на его параметры. Проверяет условие истинное или нет.
В случае с 'if - else' - позволяет проверить несколько объектов на множество параметров.
В случае с 'switch - case' - позволяет проверить один объект на множество параметров.
If - else - может быть сколько угодно, Switch - только 1, case - сколько угодно.
Пример if-else и Пример switch-case

Methods | Методы

В Джаве есть различные методы и применять их стоит в зависимости от значений внутри.
Методы бывают:
- Статические методы (эти методы принадлежат классу и вызываются через класс)
- Метод конструктора (эти методы используются для создания объектов класса, обычно имеют такое же название, как и класс, вызываются с использованием оператора new.
- методы доступа Getter Setter (эти методы используются для получения (getter) и установки (setter) значений полей объекта. Используется в PRIVATE методах, где есть условия старше 18+, к прмиеру. И с помощью Set'a - мы можем установить условие, типо старше 18+ - есть доступ, младше - нет доступа.
- Абстрактные методы (эти методы объявляются в абстрактных классах и не реализуются).

Конструкторы в Java

Конструктор — это специальный метод, который используется для создания объектов и инициализации их начальных значений. Конструкторы вызываются автоматически при создании нового экземпляра класса.

Основные характеристики конструкторов:

  • Имя конструктора совпадает с именем класса.
  • Конструктор не имеет возвращаемого типа, даже void.
  • Конструкторы могут быть перегружены (иметь разное количество параметров).

Типы конструкторов:
1. Конструктор по умолчанию: Если в классе не задан ни один конструктор, компилятор автоматически создает конструктор по умолчанию без параметров.
public class Example {
  public Example() {
    // Конструктор по умолчанию
  }
}


2. Пользовательский конструктор: Определяется программистом и может принимать параметры для инициализации полей объекта.
public class Person {
  private String name;
  private int age;

  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
}


Конструкторы играют ключевую роль в инициализации объектов и обеспечении гибкости в их создании.

Сигнатура метода

Это то, что включает в себя метод. Сигнатура метода включает в себя следующие элементы:
• модификатор доступа (public, private)
• тип данных (int, float)
• имя переменной
• параметры
Например:
public int addNumbers(int num1, int num2)
В данном примере:
"addNumbers" - это имя метода.
"int" - это тип возвращаемого значения (целое число).
"(int num1, int num2)" - это список параметров, включающий два параметра с их типами и именами.

Arrays | Массивы

Массивы – это структура данных, в ней хранятся какие-то данные.
Создание массива:
Чтобы создать массив, я должен выделить память для него и указать его размер. Например:
myArray = new int[5]; // Создание массива целых чисел длиной 5 элементов
doubleArray = new double[10]; // Создание массива чисел с плавающей запятой длиной 10 элементов
stringArray = new String[3]; // Создание массива строк длиной 3 элемента

Aggregation and Composition

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

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

Лямбда-выражения — это компактный способ описания анонимных функций (или методов) в Java. Они позволяют передавать поведение в качестве параметра и упрощают работу с функциональными интерфейсами (интерфейсы с одним абстрактным методом, например, Runnable, Comparator).

Синтаксис: (параметры) -> { тело }

Пример использования лямбда-выражения:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));


Здесь лямбда выражение name -> System.out.println(name) заменяет использование анонимного класса для реализации метода accept интерфейса Consumer.

Лямбда-выражения делают код более лаконичным и читаемым, особенно при работе с коллекциями и потоками (Streams).

Стримы в Java

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

Основные возможности стримов:

  • Фильтрация данных (filter)
  • Преобразование данных (map)
  • Сортировка (sorted)
  • Сбор результатов (collect)
  • Поиск и агрегация (например, findFirst, count)

Пример использования стримов:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> filteredNames = names.stream()
    .filter(name -> name.startsWith("A"))
    .collect(Collectors.toList());


Здесь мы используем стрим, чтобы отфильтровать имена, которые начинаются с буквы "A", и собрать их в новый список.

Стримы помогают писать более чистый и читабельный код, особенно при работе с большими объемами данных.

Функциональные интерфейсы в Java

Функциональные интерфейсы — это интерфейсы с одним абстрактным методом. Они используются в Java для реализации лямбда-выражений и метода ссылок.

Основное правило: функциональный интерфейс должен содержать только один абстрактный метод. Это позволяет использовать его как цель для лямбда-выражения.

Пример функционального интерфейса:
@FunctionalInterface
public interface MyFunctionalInterface {
  void doSomething();
}

Встроенные функциональные интерфейсы:

  • Consumer<T> — принимает объект и не возвращает результат (используется для операций с побочными эффектами).
  • Supplier<T> — не принимает аргументов, но возвращает результат.
  • Function<T, R> — принимает объект типа T и возвращает объект типа R.
  • Predicate<T> — принимает объект типа T и возвращает boolean (используется для проверки условий).

Пример использования:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name)); // Использование Consumer


Функциональные интерфейсы делают код более гибким и позволяют писать лаконичные и читабельные программы.

Дефолтная реализация методов в интерфейсах

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

DRY KISS SOLID

Это принципы для упрощения написания кода.
DRY – Don't repeat yourself – принцип призывает Вас не повторяться при написании кода. Все что Вы пишите в проекте, должно быть определено только один раз.
KISS – keep it simple stupid – делайте вещи проще. Порой наиболее правильное решение – это наиболее простая реализация задачи, в которой нет ничего лишнего.
SOLID — это принципы разработки программного обеспечения, следуя которым Вы получите хороший код, который в дальнейшем будет хорошо масштабироваться и поддерживаться в рабочем состоянии.
S - Single Responsibility Principle - Каждый класс должен иметь только одну зону ответственности.
O - Open closed Principle - Классы должны быть открыты для расширения, но закрыты для изменения.
L - Liskov substitution Principle - Дочерний класс не должен нарушать логику родительского класса, при этом работа программы не должна измениться.
I - Interface Segregation Principle - Каждый интерфейс должен быть наделенным смыслом.
D - Dependency Inversion Principle - Модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те, и другие должны зависеть от абстракции. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Что делать, чтоб ускорить манаул тесты?

В случае задания, где нужно ускорить мануальные тесты, нужно:
• Выставить приоритетность
• Тестируем новые добавленые фичи
• Ре-тест
• Поговорить с руководством, чтоб давали умеренное количество тестов.
Дело всегда опирается в коммуникацию, нужно все обсуждать.

Аннотации в Java

Аннотации в Java — это специальные метки или инструкции, которые можно добавить к коду (классам, методам, переменным, параметрам). Они помогают передавать метаданные и инструктировать компилятор или среду выполнения.

Основные виды аннотаций:

  • @Override — указывает, что метод переопределяет метод суперкласса.
  • @Deprecated — помечает метод или класс как устаревший, чтобы избежать его использования.
  • @SuppressWarnings — подавляет определенные предупреждения компилятора.
  • @FunctionalInterface — указывает, что интерфейс является функциональным и должен содержать только один абстрактный метод.

Пример использования аннотации:
public class Example {
  @Override
  public String toString() {
    return "Example";
  }
}


Пользовательские аннотации: Вы можете создавать свои собственные аннотации.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
  String value();
}

Применение:
@MyAnnotation("Test method")
public void test() {
  // Код метода
}


Аннотации позволяют сделать код более выразительным и помогают в настройке поведения программ на этапе компиляции или выполнения.

Работа с файлами и ввод/вывод (IO/NIO)

IO (Input/Output) — традиционная библиотека Java для работы с файлами и потоками данных.

Основные классы:

  • File — работа с файлами и директориями.
  • FileInputStream / FileOutputStream — чтение и запись байтов в файлы.
  • BufferedReader / BufferedWriter — чтение и запись текста с буферизацией.

NIO (New IO) — улучшенная библиотека для работы с файлами и потоками, с акцентом на производительность.

Особенности NIO:
  • Channels — более производительные альтернативы потокам.
  • Buffers — для эффективного чтения и записи данных.
  • Path и Files — современные классы для работы с файловой системой.

Многопоточность и конкурентное программирование

Многопоточность — способность программы выполнять несколько потоков одновременно.

Основные классы:

  • Thread — класс для создания и управления потоками.
  • Runnable — интерфейс для определения задачи, выполняемой в потоке.
  • ExecutorService — управление пулом потоков.

Конкурентное программирование — обработка нескольких задач одновременно для повышения производительности и эффективности.

Синхронизация:
  • synchronized — для обеспечения атомарности операций.
  • Lock — более гибкий механизм блокировки потоков.

Важные классы:
  • ConcurrentHashMap — потокобезопасный вариант HashMap.
  • CopyOnWriteArrayList — потокобезопасная реализация ArrayList.
  • CountDownLatch, CyclicBarrier — для синхронизации потоков.

JDK, JRE and JVM | компоненты платформы Java

JDK – java development kit – это набор инструментов для разработки в Джаве (он включает в себя компилятор, отладку и библиотеки).
JRE – java runtime environment – окружение выполнения Джава приложений (нужен для конечных пользователей), нужен для запуска тестов, в него входит JVM.
JVM – java virtual machine – виртуальная машина, то есть это компонент для запуска тестов.