Библиотека Java разработчика
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP
إظهار المزيد📈 نظرة تحليلية على قناة تيليجرام Библиотека Java разработчика
تُعد قناة Библиотека Java разработчика (@bookjava) في القطاع اللغوي الروسية لاعباً نشطاً. يضم المجتمع حالياً 10 280 مشتركاً، محتلاً المرتبة 12 030 في فئة التكنولوجيات والتطبيقات والمرتبة 63 913 في منطقة روسيا.
📊 مؤشرات الجمهور والحراك
منذ تأسيسه في невідомо، حقق المشروع نمواً سريعاً وجمع 10 280 مشتركاً.
بحسب آخر البيانات بتاريخ 05 يونيو, 2026، تحافظ القناة على نشاط مستقر. خلال آخر 30 يوماً تغيّر عدد الأعضاء بمقدار 20، وفي آخر 24 ساعة بمقدار 0، مع بقاء الوصول العام مرتفعاً.
- حالة التحقق: غير موثّقة
- معدل التفاعل (ER): يبلغ متوسط تفاعل الجمهور 8.29%. وخلال أول 24 ساعة من النشر يحصد المحتوى عادةً 3.77% من ردود الفعل نسبةً إلى إجمالي المشتركين.
- وصول المنشورات: يحصل كل منشور على متوسط 852 مشاهدة. وخلال اليوم الأول يجمع عادةً 388 مشاهدة.
- التفاعلات والاستجابة: يتفاعل الجمهور بانتظام؛ متوسط التفاعلات لكل منشور يبلغ 6.
- الاهتمامات الموضوعية: يركز المحتوى على مواضيع رئيسية مثل string, интерфейс, строка, boot, api.
📝 الوصف وسياسة المحتوى
يصف المؤلف القناة بأنها مساحة للتعبير عن الآراء الذاتية:
“📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.
По всем вопросам @evgenycarter
РКН clck.ru/3KoGeP”
بفضل وتيرة التحديث المرتفعة (أحدث البيانات بتاريخ 06 يونيو, 2026) تحافظ القناة على حداثتها ومستوى وصول مرتفع. وتُظهر التحليلات تفاعلاً نشطاً من الجمهور، ما يجعلها نقطة تأثير مهمة ضمن فئة التكنولوجيات والتطبيقات.
Set.of() в Java нельзя добавить дубликаты и null?
Когда используешь Set.of(...), можно столкнуться с двумя неожиданностями:
1. ❌ Нельзя добавлять дубликаты
2. ❌ Нельзя добавлять null
Разбираемся, почему так:
🔐 Set.of(...) — это immutable Set
Метод Set.of(...), добавленный в Java 9, создаёт неизменяемое множество. Это значит:
* После создания ты не можешь изменить его (добавить, удалить элемент).
* Все элементы внутри должны быть уникальны и не должны быть null.
📛 Почему нельзя дубликаты?
Потому что Set по определению — это коллекция уникальных элементов.
А Set.of(...) бросает IllegalArgumentException, сразу во время создания, если переданы дубликаты:
Set.of("a", "b", "a"); // 💥 Бросит исключение!
Это сделано для того, чтобы не было тихих ошибок — чтобы ты сразу увидел, что передал неуникальные значения.
🕳️ Почему нельзя null?
Set.of(...) не принимает null, потому что он реализован через внутренние immutable структуры, которые не допускают null`-значений. При попытке добавить `null получишь NullPointerException.
Set.of("a", null); // 💥 NullPointerException
Это сделано сознательно — null может вести к неочевидным багам и плохо сочетается с концепцией неизменяемых коллекций.
👀 Хочешь изменяемый Set, который принимает null и дубликаты фильтрует сам? Используй HashSet:
Set<String> set = new HashSet<>();
set.add(null); // ✅ Можно
👉@BookJava@Transactional на private-методах!
Очень частый анти-паттерн, который легко упустить 👇
@Service
public class UserService {
@Transactional
private void saveUser(User user) {
userRepository.save(user);
}
public void create() {
saveUser(new User());
}
}
Кажется, всё ок. Но ❌ транзакция НЕ работает.
📌 Почему?
Spring AOP использует прокси, а прокси не “видит” вызовы private-методов внутри класса. Такие вызовы происходят напрямую, мимо прокси-обёртки — и аннотация @Transactional просто игнорируется.
💡 Решение:
1. Сделай метод public и вызывай его извне (или из другого бина).
2. Или выдели этот метод в отдельный бин-сервис.
Пример:
@Service
public class UserTransactionalHelper {
@Transactional
public void saveUser(User user) {
userRepository.save(user);
}
}
И в UserService:
public void create() {
helper.saveUser(new User());
}
⚠️ Так же не работают protected, private, final, static, и @PostConstruct-методы.
Нужно помнить: Spring AOP = прокси, а значит, работают только публичные методы, вызываемые ИЗВНЕ.
👉@BookJavatoList() в Java 16+: можно ли заменить collect(Collectors.toList()) на просто .toList()?
Да, но с нюансами.
📌 Короткий ответ:
Если ты используешь Java 16+, можешь заменить:
List<String> list = stream.collect(Collectors.toList());
на:
List<String> list = stream.toList();
💡 Но будь осторожен. Вот 3 ключевых отличия:
1️⃣ Немодифицируемость
* .toList() возвращает немодифицируемый список (immutable).
* Collectors.toList() возвращает modifiable ArrayList.
var list1 = List.of("a", "b");
var list2 = list1.stream().toList();
list2.add("c"); // 💥 UnsupportedOperationException
var list3 = list1.stream().collect(Collectors.toList());
list3.add("c"); // ✅ OK
2️⃣ Тип возвращаемого списка
* Collectors.toList() — это ArrayList (или его сабкласс).
* .toList() — это неопределённый тип внутри JDK (часто List.of под капотом).
Если ты делаешь что-то вроде:
if (list instanceof ArrayList) ...
то поведение может измениться.
3️⃣ Параллельные стримы
.toList() оптимизирован для параллельных стримов: может работать быстрее, но также может повлиять на порядок, если ты этого явно не контролируешь.
⚠️ Когда НЕ стоит заменять:
* Если ты мутируешь список после получения.
* Если ты полагаешься на конкретный тип (например, ArrayList).
* Если ты используешь Java < 16.
✅ Когда заменить можно:
* Если тебе нужен read-only список.
* Если ты не изменяешь коллекцию.
* Если важна сжатость и выразительность.
📌 Резюме:
Заменяй👉@BookJavacollect(Collectors.toList())на.toList(), только если тебе действительно не нужен изменяемый список. Это безопасно при соблюдении условий, но может привести к неожиданным багам в тестах и проде, если забыть про неизменяемость.
instanceof (Java 14+) в Java 17+ позволяют писать лаконичный и безопасный код:
📌 Запись DTO с валидацией через компактный конструктор:
public record User(String name, String email) {
public User {
Objects.requireNonNull(name, "name не должен быть null");
if (!email.contains("@")) {
throw new IllegalArgumentException("Неверный email: " + email);
}
}
}
– автоматические toString(), equals(), hashCode() без лишнего кода.
🧠 Проверка и приведение типов в одном выражении:
Object obj = …;
if (obj instanceof User u) {
System.out.println("Привет, " + u.name());
}
– нет лишних кастов, код чище и безопаснее.
💡 Совет: для полей — коллекций или массивов — избегайте поверхностной мутабельности:
public record Team(String name, List<String> members) {
public Team {
members = List.copyOf(members);
}
}
– таким образом members нельзя изменить извне.
⚠️ Антипаттерн: не используйте public record X(List<String> list) без копирования — рискуете нарушить неизменяемость!
👉@BookJavaapplication.yml или properties:
spring:
jpa:
properties:
hibernate.jdbc.batch_size: 50 # размер пакета
hibernate.order_inserts: true # группировка INSERT’ов
hibernate.order_updates: true # группировка UPDATE’ов
Это позволит драйверу посылать пачками, а Hibernate — сортировать операции для максимальной эффективности.
💡 Сниппет для batch-пакетов
@Service
@RequiredArgsConstructor
public class OrderService {
private final EntityManager em;
private static final int BATCH_SIZE = 50;
@Transactional
public void saveAll(List<Order> orders) {
for (int i = 0; i < orders.size(); i++) {
em.persist(orders.get(i));
if (i > 0 && i % BATCH_SIZE == 0) {
em.flush();
em.clear(); // освобождаем persistence-context
}
}
em.flush();
em.clear();
}
}
– flush() выталкивает пакеты в БД,
– clear() освобождает ОЗУ от управляемых сущностей.
⚠️ Важные моменты
* GenerationType.IDENTITY отключает batching. Используйте @SequenceGenerator с allocationSize.
* При двусторонних связях (OneToMany) избегайте каскадного сохранения огромных графов — лучше сохранять “плоско” и затем связывать.
* Следите за JDBC-драйвером: не все поддерживают batch-вставки одинаково хорошо.
💡 Совет по мониторингу
Запустите приложение с -Dorg.hibernate.SQL=DEBUG и -Dhibernate.format_sql=true — вы увидите групповые INSERT вместо множества одиночных.
📌 Результат
* Скорость записи растёт в 5–10× (в зависимости от нагрузки).
* Память на стороне приложения остаётся стабильной, без роста Persistence Context.
👉@BookJava
--add-modules jdk.crac
--enable-preview
2. Реализуйте CheckpointListener для чистки и восстановления ресурсов:
import jdk.crac.Core;
import jdk.crac.Control;
import jdk.crac.CheckpointListener;
import jdk.crac.Context;
import org.springframework.stereotype.Component;
@Component
public class CracHandler implements CheckpointListener {
@Override
public void beforeCheckpoint(Context<?> ctx) {
// 📌 Закрываем пулы, Flush в БД, отписываемся от очередей
}
@Override
public void afterRestore(Context<?> ctx) {
// 💡 Реинициализируем пулы, повторная регистрация listeners
}
}
// Регистрация слушателя
Core.getGlobalContext().register(new CracHandler());
3. Сборка и запуск:
# Сохраняем checkpoint
java \
--add-modules jdk.crac \
--enable-preview \
-XX:CRaCCheckpointToDir=crac-checkpoint \
-jar app.jar
# Восстанавливаем из него
java \
--add-modules jdk.crac \
--enable-preview \
-XX:CRaCRestoreFrom=crac-checkpoint \
-jar app.jar
⚠️ Ограничения и нюансы
* Не все native-библиотеки безопасны для снапшота.
* Тяжёлые background-потоки: до checkpoint лучше останавливать.
* Проверяйте на staging-окружении — subtle bugs могут всплыть только после restore.
📌 Зачем это нужно?
* 🚀 Ускоренный cold-start для Spring Boot 3+ сервисов (лучшая DevOps-интеграция в контейнерах и serverless).
* 💰 Экономия ресурсов в автоскейлируемых кластерах.
Простой CRaC-proof-of-concept позволит вам измерить прирост старта ваших микросервисов уже сегодня!
👉@BookJavaStream.toList(), который собирает элементы потока в список. Раньше мы писали:
List<String> list = stream.collect(Collectors.toList());
Теперь можно укоротить до:
List<String> list = stream.toList();
💡 Главные отличия:
1️⃣ Неизменяемость
toList() возвращает unmodifiable List — любые add()/remove() вылетят UnsupportedOperationException.
Если нужен изменяемый список, продолжайте использовать collect(Collectors.toList()) или
stream.collect(Collectors.toCollection(ArrayList::new));
2️⃣ Null-элементы
toList() не допускает null и бросит NPE при встрече null в потоке. Collectors.toList() сохранит null без ошибок.
3️⃣ Спецификация
Stream.toList() гарантированно создаёт новый список с точным размером, а Collectors.toList() лишь «может» вернуть любой List (часто ArrayList, но без чётких гарантий).
⚠️ Если вам важна мутабельность или поддержка null — не меняйте на toList().
✅ Если же нужен чистый readonly-список и вы уверены в отсутствии null — смело переходите на toList() для более лаконичного и потенциально более эффективного кода.
Я перехожу на toList() везде, где нужен только чтение — получилось короче и понятнее.
👉@BookJavaEnumMap<K, V> хранит данные в массиве → нет хеширования и boxing’а.
EnumMap<Status, String> map = new EnumMap<>(Status.class);
map.put(Status.STARTED, "Запущен");
String status = map.get(Status.STARTED);
⚠️ Работает только для enum-ключей.
🧠 Прямой массив для плотных int-ключей
📌 Используйте массив вместо Map, если ключи — диапазон [0…MAX] и известны заранее.
int MAX = 1000;
var cache = new String[MAX + 1];
cache[42] = "ответ";
String result = cache[42];
💡 O(1), без коллизий и аллокаций объектов.
⚠️ Память линейно зависит от MAX.
🧠 Специализированные коллекции для примитивов
📌 Библиотеки fastutil, HPPC, Trove и др.
var fastMap = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<String>();
fastMap.put(42, "ответ");
String res = fastMap.get(42);
💡 Избегаете boxing/unboxing и получаете быстрее операции.
🧠 Switch-expression / tableswitch
📌 Для фиксированного набора целых или enum-ключей генерируйте switch, а не Map.
String handle(int code) {
return switch (code) {
case 100 -> "OK";
case 200 -> "Created";
default -> "Other";
};
}
💡 JIT компилирует switch в tableswitch или lookupswitch — молниеносно.
💡 Выбирайте стратегию под конкретную задачу:
- EnumMap — enum-ключи
- Массив — плотные int-диапазоны
- fastutil/HPPC — примитивы с большим диапазоном
- Switch — фиксированный набор значений
👉@BookJavaSystem.out.printf() с пояснениями по флагам, ширинe и точности форматирования, а также показано, как легко создавать динамический и читаемый вывод в приложениях на Java.
https://springframework.guru/java-output-printf-method/
👉@BookJavaapplication.yml:
Это обеспечивает более чистые, структурированные логи, что делает их проще для разбора инструментами вроде ELK, Grafana или Datadog.
👉@BookJava
متاح الآن! بحث تيليغرام 2025 — أهم رؤى العام 
