bookjava | Unsorted

Telegram-канал bookjava - Библиотека Java разработчика

10986

📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP

Subscribe to a channel

Библиотека Java разработчика

🧵 Виртуальные потоки: Революция производительности

Представьте, что вы строите высоконагруженный сервер. Раньше у вас было два пути:

1. Классика (Thread): Простой код, но один поток весит ~2 Мб памяти. Создадите 5,000 потоков - сервер упадет с OutOfMemoryError.
2. Асинхронность (WebFlux/Netty): Сервер держит 100k соединений, но код превращается в лапшу из callbacks и CompletableFuture, которую невозможно отлаживать.

В Java 21 появились Виртуальные потоки. Они объединяют простоту первого подхода и производительность второго.

🪶 В чем магия?

Классический поток Java (Platform Thread) привязан 1-к-1 к потоку операционной системы (OS Thread). Это дорогой ресурс.

Виртуальный поток, это просто объект в куче (heap) JVM. Он не привязан к ОС намертво.

🔴Вес: Несколько килобайт (вместо мегабайт).
🔴Количество: Можно создать миллион виртуальных потоков на обычном ноутбуке.

⚙️ Как это работает (Carrier Threads)

Под капотом работает схема Mount/Unmount:

1. JVM запускает небольшой пул обычных потоков ОС (называются Carrier Threads, обычно их число = числу ядер CPU).
2. Ваш виртуальный поток "садится верхом" на Carrier-поток и выполняет код.
3. ⚠️ Самое важное: Как только ваш код блокируется (ждет ответа от БД, читает файл, делает Thread.sleep), JVM снимает виртуальный поток с ядра.
4. Поток ОС освобождается и тут же берет в работу другой виртуальный поток.

Итог: Ядра процессора молотят на 100%, никогда не простаивая в ожидании ввода-вывода.

💻 Код: Найди 1 отличие

API практически не изменился. Вам не нужно учить новые фреймворки.


// Старый способ (Тяжелый поток ОС)
Thread.ofPlatform().start(() -> {
System.out.println("Я ем много памяти!");
});

// Новый способ (Легкий виртуальный поток)
Thread.ofVirtual().start(() -> {
System.out.println("Я ничего не вешу!");
});

// Использование с ExecutorService (для старого кода)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 1_000_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(1000); // Блокировка теперь БЕСПЛАТНАЯ
return i;
});
});
}
// Этот код запустит миллион задач за секунду, не положив сервер.



⚰️ Конец Reactive Programming?

Многие эксперты говорят: Да.
Смысл использования сложных реактивных библиотек (RxJava, Reactor) был в том, чтобы не блокировать потоки. Виртуальные потоки делают блокировку дешевой.
Теперь вы можете писать простой, последовательный код:
var user = db.findUser();
var data = http.sendRequest(user);
...и он будет работать так же эффективно, как сложный асинхронный код.

⚠️ Когда НЕ использовать?

Виртуальные потоки идеальны для I/O задач (ждать сеть, ждать диск).
Они бесполезны для CPU-Intensive задач (майнинг, шифрование, обработка видео). Если поток не ждет, а считает, он занимает поток ОС, и виртуалка тут не поможет.

🔥 Итог
Project Loom вернул нам девиз: "Один запрос - Один поток".
Больше никаких пулов потоков с ограниченным размером. Просто создавайте новый виртуальный поток для каждой задачи.

#Java #Loom #VirtualThreads #Concurrency #HighLoad

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🎯 Открытый урок «Сетевой чат на C#».

🗓 22 января в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «C# Developer».

На вебинаре:
✔️ Рассмотрим написание сетевого приложения на C#.
✔️ Мы реализуем простые клиент и сервер с помощью одного из сетевых протоколов.
✔️Также затронем темы многопточности и асинхронности

Кому будет полезно:
- Вебинар будет полезен начинающим разработчикам, желающим разобраться в сетевом и многопочном\асинхронном программировании.

Что вы получите:
- По итогам вебинара смогут проектировать сетевые приложения.
- Получат представление о работе сетевых протоколов, и многопоточности\асинхронности в приложениях.
- На практике попробуют разработать такое приложение.

🔗 Ссылка на регистрацию: https://vk.cc/cTqyyY

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

🧑‍💻Пишете на Vue и давно работаете с Vue Router по привычке? Сейчас в экосистеме появляется новая опция — Kitbag Router. Лёгкий повод пересобрать подход к роутингу и обновить стек.

На открытом уроке разберём, как подключить его к проекту, настроить под свой стек и чем он принципиально отличается от Vue Router. Пошагово пройдём путь от установки до рабочих маршрутов в SPA.

Вы познакомитесь с новой библиотекой роутинга для VueJS, научитесь создавать приложения с клиентским роутингом на Kitbag Router, сравнивать его с Vue Router и осознанно выбирать инструмент под задачу.

📆Встречаемся 21 января в 20:00 МСК в преддверие старта курса «Vue.js разработчик». Регистрация открыта: https://vk.cc/cTo0LX

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

💿 Java Records: Убийцы бойлерплейта

Сколько раз вы создавали класс просто чтобы "перенести данные" из точки А в точку Б?
Вы пишете 3 поля, а потом IDE генерирует вам 50 строк кода: конструктор, геттеры, equals, hashCode, toString... 🤯

В Java 16+ этому положили конец. Встречайте Records.

📉 Было vs Стало

Допустим, нам нужен простой DTO для пользователя.

❌ Классический POJO (Java 1.0 - 15):


public class User {
private final String name;
private final int age;

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

public String getName() { return name; }
public int getAge() { return age; }

// + equals()
// + hashCode()
// + toString() ... еще 30 строк кода
}



✅ Record (Java 16+):


public record User(String name, int age) {}



Всё. Это одна строка. 🔥

⚙️ Что происходит под капотом?

Компилятор делает всю грязную работу за вас. Создавая record, вы автоматически получаете:

1. Приватные финальные поля (private final).
2. Конструктор со всеми аргументами.
3. Геттеры (без префикса get! Просто .name(), .age()).
4. equals() и hashCode() (идеально для ключей в Map или Set).
5. toString() (красивый вывод: User[name=Alex, age=25]).

🛠 Кастомизация (Compact Constructor)

"А что, если мне нужна валидация? Нельзя же создать юзера с отрицательным возрастом!"
Для этого есть Компактный конструктор. Вам даже не нужно перечислять аргументы:


public record User(String name, int age) {
// Компактный конструктор
public User {
if (age < 0) {
throw new IllegalArgumentException("Возраст не может быть меньше 0");
}
// Присваивание this.age = age происходит автоматически!
}
}



⚠️ Ограничения (Важно знать)

Records, это не замена обычным классам во всем.

1. Они неизменяемы (Immutable). Сеттеров нет и не будет. Хотите поменять поле? Создавайте новый объект.
2. Нет наследования. Record не может наследовать (extends) другой класс (потому что он уже наследует java.lang.Record). Но имплементировать интерфейсы (implements) можно!

💡 Когда использовать?

🔴DTO (Data Transfer Objects): Ответы от API, запросы в БД.
🔴Ключи для Map: Благодаря гарантированному equals/hashCode.
🔴Внутри методов: Можно объявлять локальные рекорды прямо внутри метода, чтобы временно сгруппировать данные.

Records делают код чище и безопаснее. Они идеально сочетаются со Stream API, где данные постоянно передаются и преобразуются.
Если ваш класс это просто "мешок с данными", превращайте его в record.

#Java #Records #NewJava #CleanCode

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🎁 Optional: Лекарство от NullPointerException

Тони Хоар назвал изобретение null своей "ошибкой на миллиард долларов". NullPointerException (NPE) - самый частый кошмар Java-разработчика.
В Java 8 появился Optional<T> - класс-обертка, который явно говорит: "Здесь значения может и не быть".

📦 Что внутри?

Представьте Optional как коробку.

🔴В ней может лежать объект (Non-empty).
🔴Или она может быть пустой (Empty).

Главное правило: Никогда не возвращайте null из метода, если можно вернуть Optional.empty().

🚫 Как делать НЕ надо

Самая частая ошибка новичка, использовать Optional как старый добрый if (x != null):


Optional<User> userOpt = findUser("Alex");

// ❌ ПЛОХО: Это тот же null-check, только сложнее
if (userOpt.isPresent()) {
System.out.println(userOpt.get().getName());
}



Метод .get() - это зло. Если коробка пуста, он бросит NoSuchElementException, и вы просто поменяли шило (NPE) на мыло.

✅ Как делать НАДО (Functional Style)

Вся мощь Optional раскрывается, когда вы строите цепочки вызовов, как в стримах.

1. Если значение есть, сделай что-то (ifPresent)


findUser("Alex").ifPresent(user -> System.out.println(user.getName()));



2. Если значения нет, верни дефолт (orElse)


// Вернет юзера или создаст нового "Guest", если не нашел
User user = findUser("Alex").orElse(new User("Guest"));



3. Если значения нет — брось ошибку (orElseThrow)


User user = findUser("Alex")
.orElseThrow(() -> new IllegalArgumentException("User not found"));



4. Преобразование внутри коробки (map)
Допустим, нам нужен не сам юзер, а его email. Если юзера нет, то и email нет.


String email = findUser("Alex")
.map(User::getEmail) // Достаем email (если юзер есть)
.map(String::toUpperCase) // В верхний регистр (если email был)
.orElse("UNKNOWN"); // Если хоть на одном этапе было пусто



⚡ Золотые правила использования

1. Только для возвращаемых значений! Не используйте Optional как тип поля в классе или аргумент метода. Это лишний оверхед и мусор в коде.
2. orElse() vs orElseGet():
🔴orElse(new Object()) - объект создается всегда, даже если он не нужен.
🔴orElseGet(() -> new Object()) - объект создается только если в коробке пусто (лениво). Используйте этот вариант для тяжелых объектов.


🔥 Итог

Optional спасает не тем, что убирает null, а тем, что заставляет вас явно обработать случай отсутствия значения.
Забудьте про .get(). Используйте .map(), .filter() и .orElse().

#Java #Optional #CleanCode #NoMoreNPE

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🛒 Collectors: Собираем урожай Stream API

Мы отфильтровали, преобразовали и отсортировали данные. Теперь их нужно сложить в коробочку, чтобы использовать дальше. Для этого существует терминальная операция .collect().

Внутрь нее мы передаем специальный объект - Collector. Чтобы не писать его вручную, в Java есть утилитный класс Collectors с готовыми решениями на все случаи жизни.

📦 Базовый набор (Must Have)

1. В список или множество
💙Collectors.toList() - собирает все в ArrayList (но тип не гарантирован).
💙Collectors.toSet() - собирает в HashSet (убирает дубликаты).
💙Java 16+ Update: Теперь можно просто писать .toList() прямо у стрима, без collect(...). Это создает неизменяемый список.


2. В строку (joining)
💙Больше не нужно мучиться с StringBuilder в циклах.
💙Collectors.joining(", ") - склеит строки через запятую.



🔑 Продвинутый уровень: Карты и Группировки

Самая мощная магия происходит здесь.

3. В Map (toMap)
Превращает список объектов в Map (ключ-значение).
💙Синтаксис: toMap(Function keyMapper, Function valueMapper)
💙Важно: Если ключи совпадут, вылетит IllegalStateException.
💙Лечение: Третий аргумент - "мердж-функция" (что делать при конфликте).


4. Группировка (groupingBy)
Аналог GROUP BY из SQL. Это киллер-фича Stream API.
💙Разделяет элементы на группы по какому-то признаку и кладет их в Map<Критерий, Список>.


💻 Примеры в коде

Представьте, у нас есть класс User(String name, String role).


List<User> users = List.of(
new User("Alex", "ADMIN"),
new User("Bob", "USER"),
new User("Charlie", "USER")
);

// 1. Склеиваем имена в одну строку
String names = users.stream()
.map(User::getName)
.collect(Collectors.joining(", "));
// Результат: "Alex, Bob, Charlie"

// 2. Превращаем в Map: Имя -> Роль
Map<String, String> userMap = users.stream()
.collect(Collectors.toMap(
User::getName, // Ключ
User::getRole // Значение
));
// Результат: {Alex=ADMIN, Bob=USER, Charlie=USER}

// 3. Группируем по Роли (SQL GROUP BY)
Map<String, List<User>> byRole = users.stream()
.collect(Collectors.groupingBy(User::getRole));
// Результат:
// {
// "ADMIN": [User(Alex)],
// "USER": [User(Bob), User(Charlie)]
// }



⚡ Полезный лайфхак

В groupingBy можно передать второй коллектор! Например, чтобы не просто сгруппировать пользователей, а сразу посчитать их количество в каждой группе:


Map<String, Long> countByRole = users.stream()
.collect(Collectors.groupingBy(
User::getRole,
Collectors.counting() // Downstream collector
));
// Результат: {ADMIN=1, USER=2}



🔥 Итог

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

#Java #StreamAPI #Collectors #JavaTips

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🗺 map() vs flatMap(): Битва трансформеров

Оба метода находятся в конвейере (Intermediate operations) и нужны для преобразования данных. Но работают они с разной геометрией.

1️⃣ map() - Один к одному (1:1)

Самый простой случай. Вы берете элемент, делаете с ним что-то и возвращаете один измененный элемент. Структура потока не меняется.

💙Логика:
💙Пример: Есть список сотрудников (Employee), нужно получить список их имен (String).
💙Аналогия: У вас есть коробка с яблоками. Вы берете каждое яблоко, чистите его и кладете обратно. В итоге у вас столько же объектов, просто они изменились.


stream.map(employee -> employee.getName()) // Был Employee, стал String



2️⃣ flatMap() - Один ко многим (1:N) + Сплющивание

Этот метод нужен, когда элементы стрима - это контейнеры (списки, массивы), и вам нужно достать содержимое наружу, "сплющив" всё в один поток.

💙Логика:
💙Пример: Есть список отделов (Department), в каждом отделе - список сотрудников (List<Employee>). Вы хотите получить один общий список всех сотрудников компании.
💙Аналогия: У вас есть коробка, внутри которой лежат маленькие коробочки с конфетами.
💙Если сделать map, у вас будет поток коробочек.
💙Если сделать flatMap, вы высыпаете конфеты из всех коробочек в одну большую кучу.



💻 Код: Почувствуйте разницу

Допустим, у нас есть "Списков списков":
List<List<String>> nestedList = List.of(List.of("A", "B"), List.of("C", "D"));

❌ Попытка с map:


// Мы получим Stream из Списков: Stream<List<String>>
nestedList.stream()
.map(list -> list.stream())
.toList();
// Результат: [[A, B], [C, D]] — Матрёшка осталась!



✅ Решение с flatMap:


// Мы получим единый Stream строк: Stream<String>
nestedList.stream()
.flatMap(list -> list.stream()) // Превращаем каждый список в стрим и сливаем
.toList();
// Результат: [A, B, C, D] — То, что нужно!



⚡ Когда что использовать?

1. map: Используем в 90% случаев. Когда нужно просто превратить А в Б (Число в Строку, Объект в Поле объекта).
2. flatMap: Используем, когда нужно убрать вложенность.
💙List<List<T>> List<T>
💙Работа с файлами (список строк из списка файлов).
💙Работа с Order List<LineItem>.


🔥 Итог

💙 map преобразует элементы.
💙flatMap преобразует и разворачивает структуру.

#Java #StreamAPI #MapVsFlatMap #InterviewQuestions

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Вопросы-ответы собеседования

Можно ли создать экземпляр абстрактного класса?
Что такое интерфейс?
Как вызвать нестатический метод в статическом?
Чем отличаются параметры от аргументов в методе?
Что такое конструктор? Как его создать и вызвать?
Что такое параметризованный конструктор?
Что такое конструктор по умолчанию?
Что такое приватный конструктор? Зачем он закрытый?
Что такое статическая переменная? Как работает static поле?
Что такое статический метод? Как вызвать static метод?

источник

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🍒 Магия двойного двоеточия: Method References

Мы уже научились писать лямбды. Но иногда даже лямбда кажется слишком громоздкой. Если ваша лямбда не делает ничего, кроме вызова одного уже существующего метода, Java позволяет использовать Method Reference (ссылку на метод).

Синтаксис простой: Класс::метод (без скобок!).

🛠 4 ситуации, когда это работает

Есть 4 основных способа использовать оператор ::. Важно понимать разницу, чтобы не путаться.

1. Ссылка на статический метод
💙 Лямбда: s -> Integer.parseInt(s)
💙 Reference: Integer::parseInt
💙 Суть: Просто перенаправляем входящий параметр в статический метод.


2. Ссылка на метод конкретного объекта
💙 Лямбда: obj -> System.out.println(obj)
💙 Reference: System.out::println
💙 Суть: У нас уже есть готовый объект (System.out), и мы вызываем его метод для каждого элемента.


3. Ссылка на метод произвольного объекта определенного типа (Самый хитрый пункт! 🤯)
💙 Лямбда: s -> s.toLowerCase()
💙 Reference: String::toLowerCase
💙 Суть: Здесь метод вызывается у самого объекта, который пришел в лямбду. Хотя синтаксис похож на статический вызов, это вызов инстанс-метода.


4. Ссылка на конструктор
💙 Лямбда: () -> new ArrayList<>()
💙 Reference: ArrayList::new
💙 Суть: Используется для создания новых объектов (часто в Stream API: Collectors.toCollection(ArrayList::new)).


💻 Пример: Было vs Стало

Смотрите, как очищается код. Допустим, у нас есть список имен:


List<String> names = Arrays.asList("Alex", "Bob", "Anna");

// ❌ Уровень 1: Анонимный класс (Олдскул)
names.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});

// ⚠️ Уровень 2: Обычная лямбда
names.forEach(s -> System.out.println(s));

// ✅ Уровень 3: Method Reference (Красота)
names.forEach(System.out::println);



🧠 Как понять, когда использовать?

Если ваша лямбда выглядит так:
x -> Class.method(x)
или
x -> x.method()
...смело меняйте её на ::.

Но! Если вам нужно изменить аргумент перед передачей (например, x -> System.out.println("Name: " + x)), то Method Reference уже не подойдет, оставайтесь на лямбде.

#Java #CleanCode #MethodReference

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

☕ Функциональные интерфейсы: Магия за кулисами Лямбд

Если вы используете лямбды (а вы наверняка их используете), значит, вы работаете с функциональными интерфейсами. Давайте разберем, что это такое, зачем нужна аннотация и какие интерфейсы вы обязаны знать наизусть.

🎯 Что это такое?

Функциональный интерфейс - это интерфейс, который содержит ровно один абстрактный метод.

Именно это ограничение позволяет компилятору превращать лямбда-выражение в экземпляр этого интерфейса.

💙 Примечание: В интерфейсе может быть сколько угодно default или static методов. Главное - только один абстрактный.

📝 Аннотация @FunctionalInterface

Ставить её над интерфейсом не обязательно, но хорошим тоном считается ставить.
Зачем? Она работает как защита от дурака: если вы или ваш коллега случайно добавите второй абстрактный метод в интерфейс, компилятор сразу выдаст ошибку, не дожидаясь падения кода в местах использования лямбд.



🧰 Шпаргалка: "Великолепная четверка"

В пакете java.util.function уже есть готовые интерфейсы на 99% случаев жизни. Не пишите свои велосипеды, пока не выучите эти:

1. Predicate <T>
💙Что делает: Проверяет условие.
💙 Метод: boolean test(T t)
💙 Где нужен: Фильтрация стримов (`filter`), проверки.


2. Consumer <T>
💙 Что делает: "Потребляет" объект, ничего не возвращая.
💙 Метод: void accept(T t)
💙 Где нужен: Вывод на экран, запись в БД, forEach.


3. Supplier <T>
💙 Что делает: "Поставляет" объект (из ниоткуда), ничего не принимая.
💙 Метод: T get()
💙 Где нужен: Ленивая инициализация, генерация значений, orElseGet.


4. Function <T, R>
💙 Что делает: Превращает объект типа T в объект типа R.
💙 Метод: R apply(T t)
💙 Где нужен: Преобразование данных, map в стримах.


💻 Пример в коде


@FunctionalInterface
interface Converter {
int stringToInt(String s);
}

public class Main {
public static void main(String[] args) {
// 1. Создаем реализацию через лямбду
Converter converter = str -> Integer.parseInt(str);

// 2. Использование стандартного Consumer
java.util.function.Consumer<Integer> print = x -> System.out.println("Result: " + x);

int result = converter.stringToInt("123");
print.accept(result); // Вывод: Result: 123
}
}



🔥 Итог

Функциональные интерфейсы - это контракт для лямбда-выражений. Используйте стандартные из java.util.function, чтобы ваш код был понятен другим разработчикам без лишних документаций.

#Java #Core #Lambda #FunctionalProgramming

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🧵 String.join(): Склеиваем строки без боли

Помните те времена, когда для объединения списка строк через запятую приходилось писать циклы, использовать StringBuilder, а потом еще и аккуратно удалять последний разделитель? 🤯

Начиная с Java 8, у нас есть элегантный статический метод String.join, который делает код чистым и читаемым.

🛠 Как это работает?

Метод принимает разделитель (delimiter) и элементы, которые нужно склеить. Элементами могут быть как просто перечисление строк (varargs), так и любая коллекция (Iterable).

1️⃣ Пример с перечислением строк:


String result = String.join(" -> ", "Wake up", "Code", "Sleep");

System.out.println(result);
// Вывод: Wake up -> Code -> Sleep


2️⃣ Пример с коллекцией (List, Set):


List<String> langs = Arrays.asList("Java", "Kotlin", "Groovy");

// Больше никаких циклов!
String output = String.join(" | ", langs);

System.out.println(output);
// Вывод: Java | Kotlin | Groovy


🧐 Важные нюансы:

- Null-safe (частично): Если сам список или массив равен null, вы получите NullPointerException. Но если null является одним из элементов списка, метод просто преобразует его в строку "null".
- Под капотом: Метод использует StringJoiner (еще один класс из Java 8), что обеспечивает неплохую производительность по сравнению с обычной конкатенацией через +.

🚀 Когда использовать?

Используйте String.join, когда у вас уже есть коллекция или массив строк, и вам нужно быстро собрать их в одну строку.

Если же вы работаете со Stream API, то лучше подойдет коллектор:
.collect(Collectors.joining(", "))


#Java #Core #Tips #CleanCode

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Совет Spring Framework💡

Вы можете инжектировать (autowire) бины, которые могут отсутствовать, обернув их в java.util.Optional. Таким образом вы сообщаете, что этот бин является необязательным, избегаете исключения, если он не существует, и можете аккуратно обработать его отсутствие с помощью Optional API.

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

☕️ Spring Core: Зачем нужна аннотация @Bean?

Если вы работаете со Spring, вы видите эту аннотацию постоянно. Но чем она отличается от простого навешивания @Component над классом? Давайте разберем.

💡 Что это такое?

Аннотация @Bean используется в методах конфигурационных классов (помеченных @Configuration). Она говорит Spring-контейнеру:

"Эй, Spring! Выполни этот метод, возьми то, что он вернет, и сохрани этот объект у себя в контексте (ApplicationContext). Управляй им как бином".


🛠 Как это выглядит?


@Configuration
public class AppConfig {

// Мы явно создаем объект и отдаем его Спрингу
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper(); // Например, библиотека Jackson
}
}




🔥 Когда использовать @Bean, а когда @Component?

Это самый частый вопрос на собеседованиях.

1. Используйте @Component@Service, @Repository), когда:

- Это ваш класс. Вы имеете доступ к исходному коду.
- Вам нужна магия автоматического сканирования (component scanning). Вы просто ставите аннотацию над классом, и Spring сам его находит.

2. Используйте @Bean, когда:

- Сторонние библиотеки. Вы не можете зайти в класс ObjectMapper (из Jackson) или AmazonS3Client и написать там @Component, потому что это чужой код (read-only). Чтобы добавить такой объект в контекст Spring, вы создаете для него метод с @Bean.
- Сложная логика создания. Если создание объекта требует условий (if/else) или сложной конфигурации, проще описать это в методе явно.

⚙️ Фишки @Bean

- Имена: По умолчанию имя бина совпадает с именем метода. Можно изменить: @Bean("myCoolBean").
- Init/Destroy: Можно указать методы, которые сработают при создании или удалении бина: @Bean(initMethod = "init", destroyMethod = "cleanup").
- Зависимости: Если методу с @Bean нужны аргументы, Spring автоматически найдет и подставит их из контекста.

Итог: @Component - для автоматизации своих классов, @Bean - для ручного контроля и чужих библиотек.


#Java #Spring #SpringBoot #Coding #Education

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Совет SpringBoot

Вы можете запустить метод в Spring @Service сразу после запуска приложения, аннотируя его с помощью
@EventListener (ApplicationReadyEvent.class). Метод не может иметь параметров. Иногда я неправильно использую его, чтобы быстро протестировать определенный метод Spring Service.

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

⚡️ Микро-оптимизация: Прощай, AtomicInteger, привет, VarHandle!

👋 Сегодня поговорим о том, как можно немного ускорить ваш код, работающий с атомарными операциями, и сделать его более "современным" с помощью VarHandle, который появился в Java 9.

🧐 Проблема с AtomicInteger

AtomicInteger - это классический способ обеспечить атомарные операции (например, инкремент) над целым числом без блокировок, используя механизм Compare-And-Swap (CAS).

Однако, у него есть небольшой недостаток: он добавляет слой косвенности. Методы типа getAndIncrement() в AtomicInteger обычно вызывают внутренние статические методы из класса sun.misc.Unsafe (или его аналогов в более новых версиях), передавая в них ссылку на объект, смещение поля и новое значение.


// Примерно так это выглядит внутри AtomicInteger
// В реальном коде это, конечно, оптимизировано, но суть та же.
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}


✨ Встречайте VarHandle!

VarHandle - это новый, более гибкий и, что самое главное, более производительный низкоуровневый API для работы с полями переменных (как экземпляров, так и статических) с заданными барьерами памяти и атомарностью.

Главное преимущество? JIT-компилятор (C2) лучше оптимизирует доступ через VarHandle, чем через старые обертки типа AtomicInteger или прямые вызовы Unsafe.

🛠 Как использовать VarHandle вместо AtomicInteger

Вместо того, чтобы хранить значение в отдельном объекте AtomicInteger, мы просто объявляем поле volatile в нашем классе и получаем VarHandle для этого поля.

1. Объявляем поле:


public class Counter {
private volatile int count = 0; // Поле должно быть volatile
// ...
}


2. Создаем VarHandle:

VarHandle нужно инициализировать один раз (обычно в статическом блоке) для доступа к полю.


private static final VarHandle COUNT_HANDLE;

static {
try {
// Получаем VarHandle для поля 'count' класса 'Counter' с типом int
COUNT_HANDLE = MethodHandles.lookup().findVarHandle(
Counter.class,
"count",
int.class
);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}


3. Выполняем атомарную операцию:

Метод getAndAdd VarHandle работает точно так же, как getAndAdd в AtomicInteger.


public int increment() {
// В отличие от AtomicInteger, где первый аргумент неявен,
// VarHandle требует первым аргументом *объект*,
// к полю которого мы обращаемся.
return (int) COUNT_HANDLE.getAndAdd(this, 1);
}


🚀 Результат микро-оптимизации

В бенчмарках (например, с использованием JMH) можно увидеть, что операции getAndAdd() через VarHandle могут быть незначительно быстрее (зачастую на 5-10%), чем те же операции через AtomicInteger, особенно под высокой нагрузкой, благодаря более эффективной генерации кода JIT-компилятором.

🛑 Важно: Это микро-оптимизация. В большинстве приложений вы не заметите разницы. Но если вы пишете критически важные фреймворки, высоконагруженные коллекции или библиотеки, где каждая наносекунда на счету, переход на VarHandle может быть оправдан.



VarHandle - это не только способ микро-оптимизации, но и стандартный, гибкий API для атомарного доступа, который заменил устаревший и менее безопасный Unsafe. Для нового кода, где требуется низкоуровневый атомарный доступ, стоит отдавать предпочтение именно ему.

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🔒 Sealed Classes: Архитектурный фейсконтроль

В ООП всегда была проблема с наследованием. Либо ваш класс открыт для всех («Наследуйся кто хочет!»), либо он закрыт наглухо (final).
Но что, если я хочу, чтобы мой класс Shape (Фигура) могли наследовать только Circle и Square, и больше никто?

Раньше это решалось костылями (пакетная видимость, скрытые конструкторы). В Java 17 появился официальный механизм: Sealed Classes.

🚧 Как это работает?

Вы помечаете класс (или интерфейс) ключевым словом sealed и после слова permits перечисляете тех, кому "можно".


// Родитель: Разрешает наследование ТОЛЬКО этим двум классам
public sealed interface Payment
permits CreditCard, Cash {
}



Теперь, если Вася из соседнего отдела попытается написать class Crypto extends Payment, компилятор ударит его по рукам. 🚫

🚦 Правило трех дорог

Наследники запечатанного класса обязаны выбрать свою судьбу. Они должны иметь один из трех модификаторов:

1. final - Цепочка наследования обрывается. Дальше наследовать нельзя.

public final class Cash implements Payment { ... }




2. sealed - Иерархия продолжается, но снова под контролем.

public sealed class CreditCard implements Payment permits Visa, MasterCard { ... }




3. non-sealed - "Открываем шлюзы". Дальше от этого класса может наследоваться кто угодно (возврат к старому поведению Java).

public non-sealed class DebitCard implements Payment { ... }





🧩 Главная фишка: Комбо со Switch

Зачем это нужно, кроме запретов? Ради безопасности.
Когда вы используете sealed иерархию в новом switch, компилятор знает все возможные варианты.

Вам больше не нужно писать ветку default!


// Метод обработки платежа
String process(Payment p) {
return switch (p) {
case CreditCard c -> "Processing card: " + c.getNumber();
case Cash c -> "Processing cash amount: " + c.getAmount();
// default НЕ НУЖЕН! Компилятор знает, что третьго не дано.
};
}



Если завтра вы добавите в permits новый класс Crypto, код перестанет компилироваться, пока вы не обработаете этот новый кейс в свитче.
Это спасает от багов, когда в бизнес-логику добавили новый тип, а обработчики обновить забыли.

🔥 Итог

Sealed Classes + Records + Switch Pattern Matching = 💎
Это "Святая Троица" современной Java. Используйте запечатанные классы для моделирования доменной области, где количество вариантов конечно и известно заранее (статусы заказа, типы платежей, виды пользователей).

#Java17 #SealedClasses #Architecture #CleanCode

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🔮 Pattern Matching в Switch: Типизируй это!

Помните этот бесконечный кошмар, когда вам приходит Object, и нужно понять, что внутри?
Раньше мы писали "лестницу" из if-else и instanceof с кучей ручного кастинга (приведения типов).

❌ Было (Боль и слезы):


Object obj = getUnknownObject();

if (obj instanceof String) {
String s = (String) obj; // Ручной каст
System.out.println("Строка: " + s.length());
} else if (obj instanceof Integer) {
Integer i = (Integer) obj; // Опять каст
System.out.println("Число: " + i);
} else if (obj instanceof Long) {
// ... и так до бесконечности
}



✅ Стало (Java 21 LTS):
Теперь switch умеет проверять типы! Больше никаких instanceof и ручных приведений. Переменная создается прямо в кейсе.


switch (obj) {
case String s -> System.out.println("Строка: " + s.length());
case Integer i -> System.out.println("Число: " + i);
case Long l -> System.out.println("Длинное число: " + l);
default -> System.out.println("Непонятно что");
}



🛡 Guarded Patterns (Охрана в кейсах)

Но это еще не всё. Часто бывает, что тип нам подходит, но нужно проверить еще и значение.
Раньше пришлось бы ставить if внутри case. Теперь у нас есть ключевое слово when.


switch (obj) {
// Попадет сюда, ТОЛЬКО если это строка И она длиннее 10 символов
case String s when s.length() > 10 ->
System.out.println("Длинная строка: " + s);

// Любая другая строка
case String s ->
System.out.println("Короткая строка: " + s);

case Integer i ->
System.out.println("Число: " + i);

default -> {}
}



👻 А как же Null?

В старом switch, если передать null, мы мгновенно получали NullPointerException.
В новом switch можно (и нужно!) обрабатывать null легально:


switch (obj) {
case String s -> System.out.println("Это строка");
case null -> System.out.println("Пришел null!"); // Никакого NPE
default -> System.out.println("Что-то другое");
}




Это не просто "сахар". Это изменение подхода к полиморфизму.
Если раньше логика часто размазывалась по методам классов (animal.makeSound()), то теперь можно собирать логику обработки разных типов в одном месте, что часто бывает удобнее при написании бизнес-логики (например, обработка разных типов ивентов или DTO).

🔥 Итог

🔴Switch теперь принимает любые объекты.
🔴Кастинг происходит автоматически (case String s).
🔴Можно уточнять условия через when.
🔴Можно безопасно ловить null.

#PatternMatching #NewJava #CleanCode

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🔀 Switch Expressions: Прощай, break!

Помните это чувство, когда забыл написать break в switch-case, и код пошел выполняться дальше, создав неуловимый баг? 😫
В современных версиях Java (стандарт с Java 14) оператор switch прокачали. Теперь это не просто управляющая конструкция, а выражение, возвращающее результат.

💀 Было (Statement)

Громоздко, опасно (fall-through), переменная вынесена наружу.


DayOfWeek day = DayOfWeek.SATURDAY;
int numLetters;

switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
numLetters = 6;
break; // Забыл break? Получи баг!
case TUESDAY:
numLetters = 7;
break;
default:
throw new IllegalStateException("Wat: " + day);
}



✅ Стало (Expression)

Лаконично, безопасно, функционально.


int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
default -> throw new IllegalStateException("Wat: " + day);
};



🚀 Главные фишки

1. Стрелочный синтаксис (->)
Если используется стрелка, переход к следующему кейсу (fall-through) отключен по умолчанию. Никаких break больше писать не надо!
2. Возврат значения
switch теперь работает как формула. Вы можете сразу присвоить результат переменной или вернуть его из метода (return switch(...)).
3. Несколько условий в одну строку
case MONDAY, FRIDAY, SUNDAY — просто перечисляем через запятую.

🛑 Сложная логика и yield

Что делать, если в одном case нужно не просто вернуть число, а выполнить несколько строк кода (например, залоггировать)?
Для этого появились фигурные скобки и ключевое слово yield.

Важно: return использовать нельзя, так как он прервет выполнение всего метода, а не только свича.


String result = switch (day) {
case SATURDAY, SUNDAY -> "Выходной";
case MONDAY -> {
System.out.println("Тяжелый день...");
yield "Будни"; // yield возвращает значение из switch
}
default -> "Будни";
};



Exhaustiveness (Полнота)

Если вы делаете switch по Enum, компилятор проверит, все ли варианты вы обработали. Если добавите в Enum новый день, а в свич - нет, код просто не скомпилируется. Это отличная защита от забывчивости!

🔥 Итог

Новый switch это чистый кайф. Он делает код компактнее и убирает целый класс ошибок, связанных с пропущенными break.

🔴Используйте ->, когда нужно просто сопоставить значение.
🔴Используйте yield, если нужна логика внутри блока.

#Java #SwitchExpression #CleanCode #NewJava

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

⌨️ Открытый урок «Spring AI: от изображения к данным. Практика распознавания документов».

🗓 15 января в 20:00 МСК

🆓 Бесплатно. Урок в рамках старта курса «Разработчик на Spring Framework».

На вебинаре:
✔️ Введение в Spring AI для обработки документов.
✔️ Настройка проекта: зависимости и конфигурация модели.
✔️ Ключевые концепции: работа с изображениями, системные промпты, OutputParser.
✔️ Сервисный слой: оркестрация шагов AI-пайплайна.
✔️ REST API для загрузки/анализа, обработка ошибок и масштабирование.

Кому будет интересно:
Java-разработчикам на Spring, backend-инженерам, архитекторам и тимлидам, которым нужно встроить распознавание документов в сервисы.

Результаты после вебинара:
Соберете простой пайплайн: загрузка изображения → извлечение текста → JSON. Поймете, где в Spring AI применять промпты и OutputParser.

🔗 Ссылка на регистрацию: https://vk.cc/cTkeIQ

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

🎯 Открытый урок «Linq на практике».

🗓 14 января в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «C# Developer».

На вебинаре будут рассмотрены:
✔️ синтаксис операторов linq;
✔️синтаксис компараторов, применяемых в linq-запросах;
✔️примеры linq-запросов для наиболее популярных коллекций.

Кому будет полезно:
- данная тема будет интересна всем, кто работает с массивами данных в рамках .NET. Вы сможете эффективно использовать простой синтаксис для наиболее частых операций применяемых в рамках работы с коллекциями.

Что вы получите:
- вы сможете писать свои linq-запросы, опираясь на синтаксис linq.Будете знать разницу при применении тех или иных методов в рамках написания linq-запросов.

🔗 Ссылка на регистрацию: https://vk.cc/cTdPQ7

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

🚀 В чем разница между HashMap и Hashtable в Java?

Если вы работаете с Java, то наверняка сталкивались с HashMap и Hashtable. Оба используются для хранения пар "ключ-значение", но между ними есть важные различия. Давайте разберемся!

1. Синхронизация (Потокобезопасность)
- Hashtable:
- Синхронизирован (потокобезопасен). Все его методы синхронизированы, то есть только один поток может работать с ним одновременно.
- Это делает Hashtable безопасным для многопоточных сред, но может снижать производительность в однопоточных сценариях.
- HashMap:
- Не синхронизирован (не потокобезопасен). Несколько потоков могут обращаться к нему одновременно, что может привести к проблемам в многопоточных средах.
- Для потокобезопасности можно использовать Collections.synchronizedMap(new HashMap<>()) или ConcurrentHashMap.


2. Null-ключи и Null-значения
- Hashtable:
- Не позволяет использовать null в качестве ключа или значения. Попытка добавить null вызовет NullPointerException.
- HashMap:
- Разрешает один null - ключ и множество null -значений.


3. Производительность
- Hashtable:
- Медленнее из-за накладных расходов на синхронизацию.
- HashMap:
- Быстрее в однопоточных средах, так как не синхронизирован.


4. Наследие
- Hashtable:
- Считается устаревшим классом (появился в Java 1.0). Не является частью Java Collections Framework.
- HashMap:
- Часть Java Collections Framework (появился в Java 1.2). Более современный и широко используемый.


5. Итерация
- Hashtable:
- Использует Enumeration для перебора ключей и значений.
- HashMap:
- Использует Iterator, который более гибкий и позволяет удалять элементы во время перебора.


6. Наследование
- Hashtable:
- Наследуется от класса Dictionary (абстрактный класс, который сейчас считается устаревшим).
- HashMap:
- Наследуется от AbstractMap, который является частью Java Collections Framework.


7. Рекомендации по использованию
- Используйте HashMap, если:
- Работаете в однопоточной среде.
- Нужна высокая производительность.
- Требуется поддержка null-ключей или значений.
- Используйте Hashtable, если:
- Нужна потокобезопасность в многопоточной среде.
- Однако в современной Java ConcurrentHashMap предпочтительнее, так как он обеспечивает лучшую производительность и масштабируемость.


Пример кода

Hashtable:


Hashtable<String, Integer> hashtable = new Hashtable<>();
hashtable.put("one", 1);
hashtable.put("two", 2);
// hashtable.put(null, 3); // Выбросит NullPointerException
System.out.println(hashtable);


HashMap:

HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("one", 1);
hashMap.put("two", 2);
hashMap.put(null, 3); // Разрешено
System.out.println(hashMap);


Итоговая таблица

| Особенность | Hashtable | HashMap |
|-------------------------|---------------------------------|-------------------------------|
| Синхронизация | Синхронизирован | Не синхронизирован |
| Null-ключи/значения | Запрещены | Разрешены |
| Производительность | Медленнее | Быстрее |
| Наследие | Устаревший (Java 1.0) | Современный (Java 1.2) |
| Итерация | Enumeration | Iterator |
| Наследование | Наследует Dictionary | Наследует AbstractMap |

💡Совет: В современной разработке на Java HashMap используется чаще. Если нужна потокобезопасность, лучше выбрать ConcurrentHashMap, а не Hashtable.

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Дорогие друзья, с Новым Годом!!!

Читать полностью…

Библиотека Java разработчика

Spring Boot Tutorial | Полный курс

В этом учебном пособии по Spring Boot рассказывается о том, как начать работу с Spring Boot и Java.

00:00 Intro
01:00 Quick Word
02:12 Spring Boot Overview
03:44 Project Overview
04:28 Spring Initializr
08:05 IntelliJ
10:29 Starting The Server
14:03 Simple API with Spring Boot
18:06 Student Class
23:19 API Layer
26:38 Business Layer
29:08 Dependency Injection
32:47 Properties file
36:15 Creating and Connecting to Database
39:48 JPA and @ Entity
42:35 JPA in Action
45:52 Amigoscode Database Courses
47:35 JPA Repository
52:20 Saving Students
58:49 @ Transient
01:03:01 Post Mapping
01:08:00 Writing Business Logic
01:12:43 Testing Post Request
01:15:35 Deleting Students
01:21:33 Exercise
01:22:53 Solution
01:26:54 Testing
01:29:41 Packaging and Running Application
01:34:52 Next steps

источник

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🚀 Функциональные интерфейсы: Level Up

В первой части мы разобрали «Великолепную четверку» (Predicate, Consumer, Supplier, Function). Но что делать, если нужно принять два аргумента? Или если тип входа и выхода совпадает, и лень писать лишний код?

Для этого в Java есть Bi-версии и Операторы.

👯 Семейство «Bi» (Два аргумента)

Стандартные интерфейсы принимают только один параметр. Если вам нужно обработать пару значений (например, ключ и значение из Map), используйте приставку Bi.

1. BiPredicate <T, U>
💙 Метод: boolean test(T t, U u)
💙 Пример: Проверить, что длина строки T больше числа U.


2. BiConsumer <T, U>
💙 Метод: void accept(T t, U u)
💙 Пример: map.forEach((k, v) -> ...) - классический пример использования.


3. BiFunction <T, U, R>
💙 Метод: R apply(T t, U u)
💙 Пример: Сложить число T и число U, получить результат R.



🔄 Семейство «Operator» (Один и тот же тип)

Часто бывает, что вы преобразуете объект, не меняя его тип (String -> String, int -> int). Писать Function<String, String> - слишком длинно.

1. UnaryOperator <T>
💙 Наследник Function<T, T>.
💙 Пример: str -> str.toUpperCase() (принимает строку, возвращает строку).


2. BinaryOperator <T>
💙 Наследник BiFunction<T, T, T>.
💙 Пример: (a, b) -> a + b (два числа на вход, одно число на выход). Именно он используется в Stream.reduce.


⚡ Осторожно с боксингом!

Дженерики (<T>) работают только с объектами. Если вы используете Function<Integer, Integer> для математики, Java будет постоянно распаковывать и запаковывать int в Integer, что бьет по производительности.

Для примитивов есть свои спецназовцы:

💙 IntPredicate, LongConsumer, DoubleFunction и т.д.
💙 Правило: Если работаете с числами - всегда ищите примитивный аналог интерфейса.

💻 Пример в коде


import java.util.function.*;

public class AdvancedLambdas {
public static void main(String[] args) {
// 1. BinaryOperator: объединяем две строки
BinaryOperator<String> concat = (s1, s2) -> s1 + " " + s2;
System.out.println(concat.apply("Hello", "Java"));

// 2. BiPredicate: проверяем, начинается ли строка с префикса
BiPredicate<String, String> startsWith = (str, prefix) -> str.startsWith(prefix);
System.out.println(startsWith.test("Telegram", "Tele")); // true

// 3. IntUnaryOperator: работаем с примитивами без лишних объектов
IntUnaryOperator square = x -> x * x;
System.out.println(square.applyAsInt(5)); // 25
}
}



🔥 Итог

💙 Нужно 2 аргумента? Ищите Bi.
💙 Тип входа и выхода совпадает? Ищите Operator.
💙 Работаете с int/long/double? Ищите интерфейсы с префиксом типа.

#Java #AdvancedJava #FunctionalProgramming

📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Использование метода String.join

Метод String.join можно использовать для объединения строк с заданным разделителем.


List<String> items = Arrays.asList("Apple", "Banana", "Cherry");
String result = String.join(", ", items);
System.out.println(result); // Output: Apple, Banana, Cherry


📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

⌨️ Открытый урок «Тестирование Spring приложений. Интеграционные тесты с контекстом. Тестирование слоя репозиториев и сервисов».

🗓 25 декабря в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Разработчик на Spring Framework».

Интеграционные тесты в Spring: тестовый контекст, репозитории (JDBC/JPA), сервисы и транзакции.

На вебинаре:
✔️Особенности интеграционных тестов с поднятием контекста Spring.
✔️Тестирование слоя репозиториев: JDBC и JPA.
✔️Работа с транзакциями на сервисном слое и подходы к тестированию.

Кому будет полезно:
Backend-разработчикам на Java, начинающим осваивать тестирование Spring-приложений.

Что вы получите:
Разберётесь в базе интеграционного тестирования со Spring-контекстом и сможете написать простые тесты для репозиториев и сервисов.

🔗 Ссылка на регистрацию: https://vk.cc/cSrDoD

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

🔍 Завтра тестовое собеседование с Java-разработчиком

17 декабря(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир →
@shortcut_sh_bot

Реклама.
О рекламодателе.

Читать полностью…

Библиотека Java разработчика

Использование лямбда-выражений и Streams:
Лямбда-выражения и streams делают код более лаконичным и читаемым.


List<String> names = Arrays.asList("John", "Jane", "Jack", "Doe");
names.stream()
.filter(name -> name.startsWith("J"))
.map(String::toUpperCase)
.forEach(System.out::println);


📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Совет

По умолчанию при обновлении сущности Hibernate обновляет все ее поля. Вы можете использовать аннотацию @DynamicUpdate на объекте
@Entity и Hibernate будет обновлять только те столбцы, которые изменились.



📲 Мы в MAX

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Использование Optional для избежания NullPointerException:
Optional — это контейнер, который может содержать или не содержать ненулевое значение.


Optional<String> optional = Optional.ofNullable(getValue());
optional.ifPresentOrElse(
value -> System.out.println("Value is present: " + value),
() -> System.out.println("Value is absent")
);


📲 Мы в MAX

👉@BookJava

Читать полностью…
Subscribe to a channel