This repository has been archived on 2024-08-23. You can view files and clone it, but cannot push or open issues or pull requests.
lessons/Java/Java.md
2024-06-03 17:00:44 +03:00

1217 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Java
## Функция main
```Java
package org.example;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
```
## Сокращения InteliJ
* psvm - public static void main
* sout - Sytem.out.println()
* serr - System.err.println()
* I - for(Object object: ){}
* fori - for (int i = 0; i< ; i++) {}
* shift + shift - поиск по проекту
* ctrl + alt + t - сниппеты для блоков кода (циклы, if, switch, try-catch)
* alt + insert - сниппеты для класса
## Строки
Строки неизменяемые (immutable) и не могут иметь наследников (final). Для изменения копии
```Java
// foo и bar - 1 объект в Pool (аля static)
String foo = "foo";
String bar = "foo";
// baz - объект в куче
String baz = new String("baz");
```
### StringBuffer и StringBuilder
Данные типы - изменяемые строки с изменениями на месте. StringBuffer синхронизирует методы, поэтому медленее, но можно работать в многопотоке. StringBuilder быстрее, но методы не синхронизирует
```Java
// Можно передать строку в инициализатор, но не обязательно
StringBuffer sb = new StringBuffer();
sb.append("foo");
sb.append(5);
```
#### Методы StringBuffer и StringBuilder
* append (смотреть выше)
* delete(int start, int end)
* insert(int offeset, String str)
* replace(int start, int end, String str)
* reverse()
* substring(int start, int end) - создаст новую строку
* toString - перевести в строку
### RegEx
RegEx - щаблон для поиска строки в тексте
#### Примеры RegEx
* "java" - только слово java подойдёт
* "\\d{3}" - 3 цифры
#### Pattern для RegEx
Pattern - скомпилированный RegEx
```Java
Pattern p = Pattern.compile("java");
p = Pattern.compile("java", Pattern.CASE_INSENSETIVE);
```
## Var
Var выбирает тип по типу данных, который мы присваеваем. Нужно инициализировать при объявлении
```Java
var x = 5;
```
## Константы
В Java константы объявляются словом *final*
```Java
final int LIMIT = 5;
```
## Массивы
Объявлять массивы можно разными способами
```Java
int nums[] = new int[] {10, 20};
int[] nums = {10, 20, 30};
```
### Многомерный массив
```Java
int[][] nums = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
```
## Параметры переменной длины
```Java
static void sum(int ...nums) {
int result = 0;
for(int n: nums) {
result += n;
}
System.out.println(result);
}
```
Все обязательные параметры до параметрвов переменной длины
## Значения по умолчанию для параметров
**Их нету!!!**
## Перегрузка методов
Перегрузка позволяет определять функции с одинаковым именем, но с разными типами и/или кол-вом параметров.
```Java
static int sum(int a, int b) {
return a + b;
}
static double sum(double a, double b) {
return a + b;
}
```
Если отличается только выходной тип, то останется только последняя имплементация
```Java
// Эту версию не получится вызвать
static int sum(int a, int b) {
return a + b;
}
// Будет вызываться эта версия
static double sum(int a, int b) {
return a + b;
}
```
## Обработка ошибок
### try-catch-finally
```Java
try {
int[] a = new int[3];
a[4] = 5;
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("Will always print this");
}
```
### *throw*
```Java
throw new Exception("Very important exception! Don't you dare not handle it");
```
### *throws*
Позволяет кинуть ошибку вверх по стаку
```Java
public static void main(String[] args) throws Exception {}
```
## OOP
OOP - бяка, Java - OOP => Java - бяка
```Java
public class Foo {
int bar;
string baz;
void fooBar() {
System.out.println("FooBaz");
}
}
```
### Модификаторы доступа
* public - везде
* private - только внутри класса
* protected - подклассы и сам класс
Если не указан, то применяется модификатор по умолчанию. Он делает переменную public в пределах данного пакета
### Конструктор и инициализатор
```Java
public class Test {
int age;
int age2;
int age3;
// Блок - инициализатор
{
age = 5
}
// Конструктор
public Test(int age2, int age3_input) {
this.age2 = age2;
age3 = age3_input; // Такое не делать, ибо не красиво
}
}
```
### Наследование
```Java
public class Foo {}
public class Bar extends Foo {}
```
Множественное наследование низя. Приватное не наследуется
#### *Override*
```Java
class Foo {
int foo() {
return 5;
}
}
class Bar extends Foo {
@Override
int foo() {
return 6;
}
}
```
Уровень видимости при *Override* не может понижаться. У переопределённого метода должен быть такой же интерфейс, как и у родителя
#### *super*
*super* позволяет обратиться к методам родительского класса
```Java
class Foo {
int foo() {
return 5;
}
}
class Bar extends Foo {
@Override
int foo() {
return super.foo();
}
}
```
#### *final* при наследовании
*final* запрещает наследование
#### Наследование конструктора
```Java
class Foo {
int a, b;
Foo(int a, int b) {
this.a = a;
this.b = b;
}
}
class Bar extends Foo {
int c;
Bar(int a, int b, int c) {
// Тут super должен быть сверху, иначе ошибка
super(a, b);
this.c = c;
}
}
```
#### *instanceof*
Позволяет проверить, создан ли объект на основе какого-то класса
```Java
class Foo {
int a, b;
Foo(int a, int b) {
this.a = a;
this.b = b;
}
}
class Bar extends Foo {
int c;
Bar(int a, int b, int c) {
// Тут super должен быть сверху, иначе ошибка
super(a, b);
this.c = c;
}
}
class Main {
public static void main() {
Bar bar = new Bar();
System.out.println(bar instanceof Foo);
}
}
```
#### Абстрактные классы
Абстрактные классы могут иметь абстрактные методы (без имплементации), но от них низя создать объект.
```Java
abstract class Car {
int speed;
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
abstract int brake();
}
class Sedan extends Car {
@Override
int brake() {
return 5;
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Низя. Класс абстрактный
Sedan sedan = new Sedan();
}
}
```
### Интерфейсы
Абстрактные классы для классов, которые имеют много общего, имеют близкую связть, а интерфейсы используются для тех, которые не имеют связи:
* Bird - абстрактный класс
* Flyable - интерфейс. Его могут имплементировать и птицы, и самолёты, и Карлсон
Отличия от классов:
* Нельзя создать экземпляр
* Не содержатся конструкторы
* Все методы абстрактные
* Все поля и *static* и *final*
* Интерфейс не расширяется классом, а реализуется
* Интерфейс может расширить множество интерфейсов
```Java
public interface Swimmable {
public void swim();
}
class Duck implements Swimmable {
public void swim() {
System.out.println("A duck is swimming");
}
}
```
Могут быть определения по умолчанию
```Java
public interface Swimmable {
default public void swim() {
System.out.println("Something is swimming")
}
}
class Duck implements Swimmable {}
```
Могут быть статические и приватные (доступные только внутри интерфейса) методы и константы
```Java
public interface Foo {
int VALUE = 5; // Подразумевается public static final т.е. публичная константная
static void foo() {
System.out.println("Foo");
}
private int bar(int a, int b) {
return a + b;
}
}
class Bar implements Foo {}
```
Интерфейсы могут расширять друг друга
```Java
interface Foo {
void foo();
}
interface Bar extends Foo {
default void bar() {
return;
}
}
class Baz implements Bar {
public void foo() {}
}
```
Интерфейсы могут быть вложены в классы
```Java
class Printer {
interface Printable {
void print();
}
}
class Journal implements Printer.Printable {
public void print() {
System.out.println("Priniting journal");
}
}
```
### Классы обёртки
*Обёртка* - класс, хранящий внутри значение примитива. Эти классы содержат полезные методы и являются не изменяемыми
#### Автораспаковка, автоупаковка
```Java
int x = 5;
Integer y = x; // Автоупаковка
x = y; // Автораспаковка
```
Автоупаковка и автораспаковка не работает для массивов
## Коллекции
Основные виды:
* *Set*
* *HashSet*
* *TreeSet*
* *SortedSet*
* *List*
* *ArrayList*
* *LinkedList*
* *Vector*
* *Stack*
* *Map*
* *HashMap*
* *TreeMap*
* *SortedMap*
* *Hashtable*
### Итераторы
Позволяет перебирать значения коллекций. Может использоваться для неупорядоченных коллекций
```Java
Set<String> set = new HashSet<String>();
set.add("A");
set.add("B");
Iterator<String> iter = set.iterator();
while (iter.hasNext()) {
String text = iter.next();
System.out.println(text);
}
```
```Java
Map<String, String> map = new HashMap<String, String>();
map.put("first", "A");
Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, String> pair = iter.next();
String key = pair.getKey();
String value = pair.getValue();
System.out.println(key + ":" + value);
}
```
Можно использовать *foreach*
```Java
Set<String> set = new HashSet<String>();
set.add("A");
set.add("B");
for (String text : set) {
System.out.println(text);
}
```
### ArrayList
```Java
ArrayList<Integer> nums = new ArrayList<int>();
nums.add(5);
nums.add(5);
nums.add(5);
nums.add(5);
nums.add(5);
System.out.println(nums.get(1));
System.out.println(nums.indexOf(5));
System.out.println(nums.contains(3));
nums.add(4, 3); // insert`ит на 4 индекс число 3
nums.set(4, 5); // Меняет число на 4 индексе на 5
System.out.println(nums.toString());
nums.clear();
nums.add(5);
nums.add(5);
nums.add(5);
Integer[] numss = nums.toArray(new Integer[0]);
nums = new ArrayList<>(Arrays.asList(numss));
Collections.sort(nums);
```
Ещё методы у Collections:
* *min*
* *max*
* *reverse*
* *shuffle*
* *swap*
* *disjoint* - если есть пересечение коллекций, то false, иначе true
#### Неизменяемый список
```Java
List<Integer> = Collections.unmodifiableList(new ArrayList<>(Arrays.asList(1, 2, 3)));
```
### LinkedList
*LinkedList* - двусвязный список
```Java
LinkedList<Integer> nums = new LinkedList<Integer>();
nums.add(5);
nums.add(5);
nums.add(5);
```
Особые методы:
* *addFirst*
* *addLast*
* *peekFirst*
* *peekLast*
* *pollFirst* - получить первый и удалить его (null, если пусто)
* *pollLast*
### Vector
*Vector* - синхронизированный *ArrayList*, который всё хранит как Object. Синтаксис страшный.
### Stack
*Stack* - LIFO
Методы:
* *empty* - возвращает bool
* *peek*
* *pop*
* *push*
* *search* - возвращает offset от вершины стека
### Deque
*Deque* - двунаправленный стек с фиксированной длинной
Методы:
* *addFirst*
* *addLast*
* *getFirst* - без удаления
* *getLast*
* *offerFirst*
* *offerLast*
* *peekFirst*
* *peekLast*
* *pollFirst* - с удалением
* *pollLast*
### ArrayDeque
*ArrayDeque* - двунаправленный стек с динамической длиной
Методы:
* *addFirst*
* *addLast*
* *getFirst* - без удаления
* *getLast*
* *pollFirst* - с удалением
* *pollLast*
### HashSet
Аля Rust. Использует метод hashCode() для получения хеша значений
Методы:
* add
* remove
* contains
* size
* clear
* isEmpty
* iterator
## Enum
```Java
enum Country {
CANADA, NETHERLANDS
}
enum Country2 {
CANADA("CAN"), NETHER("NTH"), ENGLAND
}
```
## Работа с файлами
* FileWriter
* FileReader
В конце их нужно закрывать
```Java
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws Exception {
FileReader fr = new FileReader("Foo.txt");
Scanner scanner = new Scanner(fr);
ArrayList<String> words = new ArrayList<>();
while (scanner.hasNext()) {
words.add(scanner.next());
}
Collections.sort(words);
System.out.println(words);
}
}
```
## Generic
```Java
class Account<T> { // Account<String> account = new Account<String>("FooBar");
private T id;
public int sum;
public Account(T id, int sum) {
this.id = id;
this.sum = sum;
}
public T getId() {
return id;
}
}
class Printer {
public static <T> void print(T[] items) { // Вызывать так: Printer.<String>print(new String[]{"a", "b"});
for(T item: items) {
System.out.println(item);
}
}
}
interface Acountable {
String getId();
}
class Account2 implements Acountable {
String id;
public <T>Account2(T id) { // Account account = new Account(5);
this.id = id.toString();
}
public String getId() {
return id;
}
}
class Transaction<T extends Account> { // T может быть только наследником Account
T account;
}
class Transaction2<T extends Acountable> { // T имплементирует Acountable
T account;
}
class Transaction3<T extends Account & Accountable> { // T имплементирует Accountable и наследует Account
T account;
}
```
## Records
> Записи или Records позволяют быстро содздавать классы, уменьшая количесво бойлерплейта. Записи автоматически определяют инициализатор, методы hashCode, toString и equals. Поля являются *private* и *final* и имеют одноимённые геттеры
```Java
record Person(String name, int age) { }
```
Можно добавить дополнительную логику в созданный конструктор
```Java
record Person(String name, int age) {
Person {
if(age < 1 || age > 110) {
age = 18;
}
}
}
```
Можно полностью переписать изначальный конструктор
```Java
record Person(String name, int age) {
Person(String name, int age) {
this.name = name;
if(age < 1 || age > 110) {
age = 18;
}
this.age = age;
}
}
```
Мы можем определять какие-то другие конструкторы, но они вызывать канонический конструктор
```Java
record Person(String name, int age) {
Person(String firstName, String lastName, int age) {
this(firstName + " " + lastName, age);
}
}
```
Можно также переопределить остальные методы
```Java
record Person(String name, int age) {
public String name() {
return "Mister " + name;
}
public String toString() {
return "What a fine gentleman we have here"
}
}
```
### Ограничения record
Классы *record* не могут наследовать, быть наследованными и абстрактными, но могут имплементировать интерфейсы. Нельзя добавлять дополнительные нестатические пора и инициализаторы
```Java
record Person(String name, int age) {
static int minAge;
static { // Это статический инициализатор
minAge = 18;
}
}
```
## Upcasting & Downcasting
```Java
class Person {}
class Employee {}
class Main {
public static void main(String[] args) {
Object bob = new Person(); // Upcasting
Person tom = new Employee(); // тоже upcasting
Person bob2 = (Person)bob;
}
}
```
## Comparator & Comparable
```Java
import java.util.Comparator;
import java.util.TreeSet;
class Person implements Comparable<Person> {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
String getName() {
return name;
}
int getAge() {
return age;
}
// Отриц число, если меньше, 0, если равно, и полож число, если больше
public int compareTo(Person p) {
return name.compareTo(p.getName());
}
}
class PersonNameComparator implements Comparator<Person> {
public int compare(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
}
class PersonAgeComparator implements Comparator<Person> {
public int compare(Person a, Person b) {
return a.getAge() - b.getAge();
}
}
class Main {
public static void main(String[] args) {
Comparator<Person> pcomp = (new PersonNameComparator()).thenComparing(new PersonAgeComparator());
TreeSet<Person> people = new TreeSet<Person>(pcomp);
people.add(new Person("Tom", 10));
people.add(new Person("Tam", 12));
people.add(new Person("Tim", 13));
people.add(new Person("Bill", 40));
people.add(new Person("Bob", 15));
for(Person p : people) {
System.out.println(p.getName());
}
}
}
```
## Лямбда
> Лямбда представляет набор инструкций,которые можно выделить в отдельную переменную
```Java
public class Main {
public static void main(String[] args) {
Operation op = (x, y) -> x + y;
Operation op2 = (int x, int y) -> x - y;
int result = op.calculate(10, 20);
System.out.println(result);
System.out.println(op.calculate(10, 20));
}
}
interface Operation {
int calculate(int x, int y);
}
```
Альтернативные формы:
* () -> 30 + 30
* n -> n * n
Лямбда функции могут возвращать void
```Java
interface Printer {
void print(String s);
}
class Main {
public static void main(String[] args) {
Printer p = (s) -> System.out.println(s);
}
}
```
Переменные, которые используются в лямбде нельзя менять
### Блоки кода в лямбда-функциях
```Java
public class Main {
public static void main(String[] args) {
Operation op = (int x, int y) -> {
if(x < 0) {
x = -x;
}
return x + y;
};
}
}
interface Operation {
int calculate(int x, int y);
}
```
### Обобщённые лямбды
```Java
public class Main {
public static void main(String[] args) {
Operation<Integer> op = (x, y) -> {
if(x < 0) {
x = -x;
}
return x + y;
};
}
}
interface Operation<T> {
T calculate(T x, T y);
}
```
## Ссылки на метод, как параметр методов
```Java
interface Operation {
int calculate(int x, int y);
}
class Operations {
static int Add(int x, int y) {
return x + y;
}
}
class Main {
public static void main(String[] args) {
Operation op = Operations::Add;
}
}
```
Также можно ссылаться на методы объекта, через *переменная::метод*, и на конструктор, через *класс::new*. Лямбды также можно возврощать
## Встроенные функцианальные интерфейсы
* *Predicate\<T>* - *boolean test(T t)*
* *BinaryOperator\<T>* - *T apply(T t1, T t2)*
* *UnaryOperator\<T>* - *T apply(T t)*
* *Function\<T, R>* - *R apply(T t)*
* *Consumer\<T>* - *void accept(T t)*
* *Supplier\<T>* - *T get()*
## *Java.Util*
### Класс *Locale*
> Локали - боль... страдания... *C++*
*Locale.getDefault()* чтобы получить текущую
### *Date*
> *Date* - штука для работы с датами. Можно использовать для форматирования с помощью *Locale*
### *Calendar* & *GregorianCalendar*
> *Calendar* - абстрактный класс, а *GregorianCalendar* - имплементация. Он также держит часы, минуты, секунды. Класс *DateFormat* для красивого вывода
### *TimeZone* & *SimpleTimeZone*
> *Calendar* - абстрактный класс, а *GregorianCalendar* - имплементация. Он также держит часы, минуты, секунды. Нужны для работы с часовыми поясами
### *UUID*
> *UUID* - класс для работы с UUID
```Java
UUID id = UUID.randomUUID();
```
### *StringTokenizer*
> *StringTokenizer* нужен для разделения строк на токены, используя данный разделитель (по умолчанию пробел)
## Android Studio
### Устройство платформы Android
* Базовый уровень (Linux Kernel) - firmware
* Библиотеки и среда исполнения (Libraries & Android Runtime)
* Каркас приложений (Application Framework)
* Уровень приложений (Applications)
### Комплекты разработчика
* JDK (Java Development Kit) - комплект разработчика на Java. Включает компилятор, библиотеки, примеры, документацию, утилиты и исполнительную среду
* SDK (Software Development Kit) - утилиты для создания и тестирования приложений
* SDK Manager - инструмент для заргузки компонентов Android SDK
* Android Emulator - эмулирует телефон на компьютере
* AVD Manager - графический интерфейс для создания виртуальных Android устройст
* Android Debig Bridge - управление состоянием эмулятора или реального устройства, подключённого к компьютеру
### Структура Android приложения
* *Gen* файлы генерируются Java. Являются обязательным компонентом
* *AndroidManifest.xml* - файл манифеста предоставляет системе лсновную информацию о программе
* *Src* - каталог с исходным компонентом
* *Assets* - произвольное собрание каталогов и файлов
* *Res* - катагол с ресурсами прилодения. Там подкапки drawable, anum, layout, menu, values, xml и raw
### Инициализация объектов Activity
```Java
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import com.example.myapplication.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
int counter = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
binding.textView.setText("0");
binding.textView.setTextSize(33);
binding.textView.setTextColor(Color.RED);
binding.button.setOnClickListener((foo) -> {
counter += 1;
binding.textView.setText(String.valueOf(counter));
});
}
}
```
### Переключение Activity
```Java
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
```
### Fragments
*Fragment* - подобие *activity*, но одно *activity* может содержать несколько фрагментов.
Для взаимодействия фрагментов используется *FragmentManager*, а для удаления, добавления и замены *FragmentTransaction*
Основные методы *FragmentManager*:
* *findFragmentById(int id)*
* *findFragmentByTag(String tag)*
Методы *FragmentTransaction*:
* *add*
* *remove*
* *replace*
* *hide*
* *show*
* *detach* - открепляет от GUI, но экземпляр сохраняется
* *attach* - обратный метод *detach*
Для получения экземляра *FragmentTransaction* нужно вызвать *FragmentManager.beginTransaction*. После транзакции вызывается *commit*
## Адаптеры
* *ArrayAdapter\<T>*
* *ListAdapter*
* *SpinnerAdapter*
* *SimpleAdapter*
## LayoutInflater
> Позволяет из содержимого layout файла создать *View* элемент
## Spring
### Аннотации
* *@Component* - помечает класс как bean (зависимость, которую можно использовать в проекте)
* *@Autowired* - автоматически ещет зависимости метода, конструктора и поля
* *@Primary* - помечает приорететный bean для *@Autowired*
* *@Scope*("singleton" | "prototype")
* *@PostConstruct*
* *@PreDestroy*
* *@Controller* - просто контроллер (контроллер тоже bean)
* *@RestController* - помечает класс как Bean и содержит анотацию @ResponseBody, позволяющюю преобразовывать в Json любой возвращаемый объект
* *@RequestMapping(route)* - позволяет запросы на методы контроллеров. Пример route - "/users/v1/"
* *@Service* - помечает класс с бизнес-логикой
### Qualifier
Позволяет указатать какой класс использовать для интерфейса, когда Primary не подходит. Все qualifier с маленькой буквы
```Java
public MusicPlauer(@Qualifier("rockMusic") Music music) {
music.playMusic();
}
```
### Lombok
Генерирует геттеры и сеттеры
* *@Getter*
* *@Setter*
* *@NoArgsConstructor*
* *@AllArgsConstructor*
* *@RequiredArgsConstructor*
* @Data* включает все вышеперечисленные
### Jakatra validation
* @NotNull подтверждает, что значение аннотированного свойства не является нулевым.
* @AssertTrue подтверждает, что значение аннотированного свойства является истинным.
* @Size проверяет, что аннотированное значение свойства имеет размер между атрибутами min и max. Мы можем применить его к свойствам String, Collection, Map и Array.
* @Min проверяет, что аннотированное свойство имеет значение не меньше, чем атрибут value.
* @Max проверяет, что аннотированное свойство имеет значение не больше, чем атрибут value.
* @Email подтверждает, что аннотированное свойство является действительным адресом электронной почты.
Некоторые аннотации принимают дополнительные атрибуты, но атрибут сообщения является общим для всех из них. Это сообщение, которое обычно отображается, когда значение соответствующего свойства не проверяется.
* @NotEmpty подтверждает, что свойство не является нулевым или пустым. Мы можем применить его к значениям String, Collection, Map или Array.
* @NotBlank может быть применен только к текстовым значениям и проверяет, что свойство не является нулевым или пробелом.
* @Positive и @PositiveOrZero применяются к числовым значениям и подтверждают, что они строго положительные или положительные, включая 0.
* @Negative и @NegativeOrZero применяются к числовым значениям и подтверждают, что они строго отрицательные или отрицательные, включая 0.
* @Past и @PastOrPresent подтверждают, что значение даты находится в прошлом или в прошлом, включая настоящее. Мы можем применить его к типам дат, в том числе к тем, которые добавлены в Java 8.
* @Future и @FutureOrPresent подтверждают, что значение даты находится в будущем или в будущем, включая настоящее.\
### Базы данных
* JPA описывает взаимодействие с Бд
* Hibernate - ORM
* Spring Data - унифицированный способ создания репозиториев для хранилищ разных типов