Библиотека Java разработчика
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP
Показати більше📈 Аналітичний огляд Telegram-каналу Библиотека 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”
Завдяки високій частоті оновлень (останні дані отримано 07 червня, 2026), канал підтримує актуальність та високий рівень охоплення публікацій. Аналітика показує, що аудиторія активно взаємодіє з контентом, що робить його важливою точкою впливу в категорії Технології та додатки.
record лучше class в Java?
В Java 14 появился record – новый тип классов, предназначенный для удобного хранения данных. Чем он лучше обычного class? Давайте разберёмся!
🔹 Запись против класса
Обычный класс:
class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
Много бойлерплейта…
Теперь то же самое с record:
record Person(String name, int age) {}
✅ Меньше кода
✅ Автоматически генерируются toString(), equals(), hashCode()
✅ Иммутабельность по умолчанию
⚠️ Когда НЕ стоит использовать record?
- Если нужен изменяемый объект
- Если требуется сложная бизнес-логика внутри класса
Вы уже используете record в своих проектах? Делитесь опытом! 🚀
👉@BookJavaCompletableFuture: Асинхронность в Java без боли
Привет, коллеги! Сегодня поговорим о CompletableFuture, который помогает писать асинхронный код в Java без коллбэков и потерь в читабельности.
📌 1. Почему CompletableFuture?
В Java давно есть Future, но он неудобен:
❌ Нельзя комбинировать несколько задач.
❌ Блокирует поток при вызове .get().
❌ Нет удобных методов для обработки результатов.
👉 CompletableFuture решает все эти проблемы, позволяя комбинировать задачи, обрабатывать ошибки и не блокировать потоки.
📌 2. Базовый пример использования
import java.util.concurrent.CompletableFuture;
public class AsyncExample {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
sleep(2000);
return "Привет, мир!";
});
future.thenAccept(result -> System.out.println("Результат: " + result));
System.out.println("Этот текст выведется раньше результата!");
sleep(3000); // Чтобы программа не завершилась раньше времени
}
private static void sleep(int ms) {
try { Thread.sleep(ms); } catch (InterruptedException ignored) {}
}
}
🔹 Здесь supplyAsync() выполняет задачу в другом потоке, а thenAccept() позволяет асинхронно обработать результат.
📌 3. Комбинирование нескольких задач
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<String> result = future1.thenCombine(future2, (s1, s2) -> s1 + " " + s2);
System.out.println(result.join()); // Hello World
✅ thenCombine() объединяет результаты двух асинхронных задач.
📌 4. Обработка ошибок (exceptionally)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("Что-то пошло не так!");
}
return "Все хорошо!";
}).exceptionally(ex -> "Ошибка: " + ex.getMessage());
System.out.println(future.join());
✅ Если в supplyAsync() произошла ошибка, она обработается в exceptionally(), и программа не упадёт.
📌 5. Запуск нескольких задач параллельно
CompletableFuture<Void> allTasks = CompletableFuture.allOf(
CompletableFuture.runAsync(() -> sleepAndPrint("Задача 1", 1000)),
CompletableFuture.runAsync(() -> sleepAndPrint("Задача 2", 2000)),
CompletableFuture.runAsync(() -> sleepAndPrint("Задача 3", 1500))
);
allTasks.join(); // Дождёмся завершения всех задач
private static void sleepAndPrint(String msg, int ms) {
try { Thread.sleep(ms); } catch (InterruptedException ignored) {}
System.out.println(msg);
}
✅ allOf() позволяет запустить несколько задач параллельно и дождаться их завершения.
📌 Итог
🔹 CompletableFuture – это мощный инструмент для работы с асинхронностью в Java.
🔹 Позволяет избежать блокировок, обрабатывать ошибки, комбинировать задачи.
🔹 Улучшает читаемость кода по сравнению с Future и ExecutorService.
📢 А как вы используете CompletableFuture в своих проектах? Делитесь в комментариях! 🚀
👉@BookJavaOptional<T> в Java — это мощный инструмент для работы с возможными null значениями, но часто его используют неправильно. Давайте разберём основные ошибки и лучшие практики.
❌ Плохие примеры:
1️⃣ Использование Optional как поля в классе
class User {
Optional<String> name; // ❌ Плохая практика
}
Лучше просто использовать String, а если нужно, то оборачивать значение в Optional при возврате.
2️⃣ Использование isPresent() вместо ifPresent()
if (optionalValue.isPresent()) {
process(optionalValue.get()); // ❌ Неоптимально
}
Лучше так:
optionalValue.ifPresent(this::process); // ✅ Правильный подход
📌 Хороший пример использования:
public Optional<User> findUserById(int id) {
return Optional.ofNullable(userRepository.get(id));
}
💡 Правильное использование Optional помогает избежать NullPointerException и делает код чище.
🔥 А как вы используете Optional? Пишите в комментариях! 🚀
👉@BookJavaConcurrentHashMap лучше подходит для многопоточной работы, чем HashMap, и как он работает внутри.
В отличие от HashMap, который не потокобезопасен и может приводить к бесконечным циклам при одновременной модификации, ConcurrentHashMap использует сегментированную блокировку, что позволяет работать с разными частями карты параллельно без полной блокировки всей структуры.
📌 Основные особенности:
- Делит данные на сегменты (до JDK 8 или использует synchronized и CAS операции (начиная с JDK 8).
- Чтение (get()) не требует блокировки.
- Запись (put()) использует минимально возможные блокировки.
- Нет null ключей и значений (в отличие от HashMap).
Пример использования:
import java.util.concurrent.ConcurrentHashMap;
public class Main {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("Java", 1);
map.put("Python", 2);
map.put("C++", 3);
System.out.println(map.get("Java")); // 1
}
}
А вы часто используете ConcurrentHashMap? Поделитесь в комментариях! 👇
👉@BookJavatry-catch), либо объявили его в сигнатуре метода (с помощью throws). Если этого не сделать, код не скомпилируется.
- Unchecked исключения: Не проверяются на этапе компиляции. Компилятор не требует их обработки или объявления. Обычно они возникают из-за логических ошибок в коде (например, NullPointerException, ArrayIndexOutOfBoundsException).
2. Поведение во время выполнения
- Checked исключения: Эти исключения обычно связаны с внешними факторами (например, проблемы с файловым вводом-выводом, сетевыми соединениями) и могут возникать в ходе нормального выполнения программы. Если такое исключение выбрасывается и не обрабатывается, оно будет передаваться вверх по стеку вызовов, пока не будет перехвачено или программа не завершится.
- Unchecked исключения: Эти исключения часто вызваны ошибками в коде (например, деление на ноль, обращение к null). Если такое исключение выбрасывается и не перехватывается, оно также будет передаваться вверх по стеку вызовов, но, поскольку их не требуется объявлять или обрабатывать, это может привести к неожиданному завершению программы.
3. Наследование
- Checked исключения: Все исключения, которые наследуют Exception (но не RuntimeException), являются проверяемыми.
- Unchecked исключения: Все исключения, которые наследуют RuntimeException или Error, являются непроверяемыми.
4. Примеры
- Checked исключения: IOException, SQLException, ClassNotFoundException.
- Unchecked исключения: NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException.
5. Обработка во время выполнения
- Checked исключения: Поскольку они проверяются на этапе компиляции, вы обязаны явно их обрабатывать. Это делает код более устойчивым, но может увеличить его объем.
- Unchecked исключения: Поскольку они не проверяются на этапе компиляции, их сложнее отлаживать и обрабатывать, так как они могут быть неочевидными в коде.
6. Производительность
- Нет значительной разницы в производительности между checked и unchecked исключениями во время выполнения. Стоимость выбрасывания и перехвата исключений одинакова для обоих типов.
Итог
- Checked исключения: Контролируются компилятором, должны быть обработаны или объявлены, обычно используются для recoverable (восстанавливаемых) ситуаций.
- Unchecked исключения: Не контролируются компилятором, часто возникают из-за ошибок в коде и могут привести к аварийному завершению программы, если не обработаны.
Оба типа исключений ведут себя схожим образом во время выполнения, но ключевое различие заключается в том, как они контролируются и обрабатываются на этапе разработки.
👉@BookJavaРеклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruInteger.valueOf(int) помните, что значения в диапазоне от -128 до 127 кэшируются для повышения производительности. За пределами этого диапазона создаются новые объекты.
Размер кэша можно контролировать с помощью опции -XX:AutoBoxCacheMax=<размер>. 🔥
👉@BookJavaРеклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruYearMonth, получить его первый день, а затем использовать функцию datesUntil(), которая возвращает Stream всех дней до указанной даты.
👉@BookJavaint`, `double`, `char`): передается копия значения. Изменения внутри метода не влияют на оригинальную переменную.
✅ Объекты (экземпляры классов): передается копия ссылки на объект, а не сам объект. Внутри метода можно изменить состояние объекта, но нельзя изменить саму ссылку на него.
📌 Примеры
🔹 Передача примитивов (значение не изменяется)
public class Test {
public static void modifyPrimitive(int num) {
num = 10; // Это изменение локальное
}
public static void main(String[] args) {
int x = 5;
modifyPrimitive(x);
System.out.println(x); // Выведет: 5 (не изменилось)
}
}
🔹 Передача объекта (изменение состояния объекта сохраняется)
class Person {
String name;
}
public class Test {
public static void modifyObject(Person p) {
p.name = "Alice"; // Изменяет состояние объекта
}
public static void main(String[] args) {
Person person = new Person();
person.name = "Bob";
modifyObject(person);
System.out.println(person.name); // Выведет: Alice
}
}
🔹 Переназначение ссылки (не изменяет оригинальный объект)
class Person {
String name;
}
public class Test {
public static void reassignReference(Person p) {
p = new Person(); // Переназначение ссылки (локально)
p.name = "Charlie";
}
public static void main(String[] args) {
Person person = new Person();
person.name = "Bob";
reassignReference(person);
System.out.println(person.name); // Выведет: Bob (не изменилось)
}
}
🔥 Итог
🔹 Java всегда передает данные по значению!
🔹 Примитивы передаются как копии значений.
🔹 Объекты передаются как копии ссылок, но изменения внутри объекта сохраняются.
🔹 Если внутри метода изменить саму ссылку, это не повлияет на оригинальный объект.
👉@BookJavaРеклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.rusorted() становится меньше и читается почти как естественный язык. Кроме того, вы можете использовать статический импорт.
👉@BookJavaCustomizer - велика вероятность, что вы сможете настроить bean, не отказываясь от автоконфигурации.
👉@BookJavaРеклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruif). Если предсказание верное, процессор выполняет инструкции быстро. Но если предсказание ошибочное, процессор вынужден откатить выполнение и начать заново, что замедляет работу.
- Отсортированные массивы: В отсортированном массиве данные следуют предсказуемым шаблонам. Например, если вы проверяете условие if (array[i] > threshold), результаты будут более последовательными (например, все true после определённого момента). Это помогает предсказателю ветвлений угадывать правильно, уменьшая простои.
- Неотсортированные массивы: В неотсортированном массиве результаты условных проверок более случайны. Это затрудняет работу предсказателя, увеличивая количество ошибок и замедляя выполнение.
🧠 2. Эффективность кэша
Кэш процессора — это быстрая память, которая хранит недавно использованные данные. Доступ к данным из кэша намного быстрее, чем из основной памяти.
- Отсортированные массивы: При обработке отсортированного массива данные читаются последовательно. Это улучшает эффективность кэша, так как процессор может заранее загружать соседние элементы, уменьшая количество промахов кэша.
- Неотсортированные массивы: В неотсортированном массиве доступ к данным менее предсказуем, что приводит к большему количеству промахов кэша и замедлению работы.
🛠 3. Алгоритмические оптимизации
Некоторые алгоритмы специально разработаны для работы с отсортированными данными. Например:
- Бинарный поиск: Работает только с отсортированными массивами и имеет сложность O(log n), что намного быстрее линейного поиска (**O(n)**) в неотсортированном массиве.
- Слияние массивов: Объединение двух отсортированных массивов происходит эффективнее, чем неотсортированных.
🧪 Пример: Предсказание ветвлений в действии
Рассмотрим пример кода:
int sum = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] >= 128) {
sum += array[i];
}
}
- Если массив отсортирован, условие if будет сначала всегда false, а потом всегда true. Это помогает предсказателю работать эффективно.
- Если массив неотсортирован, условие if будет выполняться хаотично, что приведёт к частым ошибкам предсказания и замедлит программу.
⏱ Бенчмаркинг
Вы можете сами проверить разницу в производительности, запустив тесты на отсортированных и неотсортированных массивах. Отсортированный массив будет обрабатываться быстрее благодаря описанным выше причинам.
🎯 Вывод
Основная причина, по которой обработка отсортированного массива быстрее, — это предсказание ветвлений. Отсортированные данные делают выполнение программы более предсказуемым, уменьшая простои процессора. Также важны эффективность кэша и алгоритмические оптимизации.
👉@BookJavaРеклама. ООО «Отус онлайн-образование», ОГРН 1177746618576public:
- Видимость: Доступен из любого другого класса.
- Использование: Когда класс, метод или поле объявлены как public, они доступны из любого класса в любом пакете.
- Пример:
public class MyClass {
public int myField;
public void myMethod() {
// Реализация метода
}
}
Здесь myField и myMethod доступны из любого другого класса.
2. protected:
- Видимость: Доступен внутри того же пакета и для подклассов (даже если они находятся в других пакетах).
- Использование: Полезно, когда нужно разрешить доступ подклассам, но ограничить доступ для несвязанных классов вне пакета.
- Пример:
public class MyClass {
protected int myField;
protected void myMethod() {
// Реализация метода
}
}
Здесь myField и myMethod доступны для любого подкласса MyClass, даже если он находится в другом пакете.
3. package-private (по умолчанию, без модификатора):
- Видимость: Доступен только внутри того же пакета.
- Использование: Если модификатор доступа не указан, класс, метод или поле считаются package-private. Это уровень доступа по умолчанию.
- Пример:
class MyClass {
int myField;
void myMethod() {
// Реализация метода
}
}
Здесь myField и myMethod доступны только для классов внутри того же пакета.
4. private:
- Видимость: Доступен только внутри того же класса.
- Использование: Используется для инкапсуляции внутренних деталей класса, предотвращая доступ извне.
- Пример:
public class MyClass {
private int myField;
private void myMethod() {
// Реализация метода
}
}
Здесь myField и myMethod доступны только внутри MyClass.
📌 Краткая таблица:
| Модификатор доступа | Тот же класс | Тот же пакет | Подклассы | Другие пакеты |
|---------------------|--------------|--------------|-----------|---------------|
| public | Да | Да | Да | Да |
| protected | Да | Да | Да | Нет |
| package-private | Да | Да | Нет | Нет |
| private | Да | Нет | Нет | Нет |
💡Основные моменты:
- Используйте public для API и методов/полей, которые должны быть доступны всем.
- Используйте protected для методов/полей, которые должны быть доступны подклассам, но не всем остальным.
- Используйте package-private для внутренней реализации, которая не должна быть доступна вне пакета.
- Используйте private для инкапсуляции, скрывая детали реализации внутри класса.
👉@BookJavaРеклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruHashMap и 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.
👉@BookJava
Вже доступно! Дослідження Telegram за 2025 — головні інсайти року 
