Java библиотека


Гео и язык канала: Россия, Русский
Категория: Технологии


Книги, статьи, мемы и многое другое для Java программиста!
По сотрудничеству и рекламе: https://t.me/NadikaKir или в ЛС сообщества ВК https://vk.com/javatutorial
Канал в перечне РКН: https://vk.cc/cJrT4A
Мы на бирже: https://telega.in/c/javalib/
Сообщество VK https://vk.com/javatutorial

Связанные каналы

Гео и язык канала
Россия, Русский
Категория
Технологии
Статистика

Java-разработчики, ConcurrentHashMap потокобезопасен.

Но он не блокирует всю коллекцию при каждом обновлении. Что же происходит, когда несколько потоков изменяют её одновременно?

До Java 8 → блокировки на уровне сегментов.
Java 8+ → CAS + синхронизация на уровне корзин.

Как это улучшает работу с конкурентным доступом? И когда ConcurrentHashMap всё ещё нужны блокировки?

Java Библиотека | Мы в Телеграм


👩‍💻 Что нового появилось в Java 27

Релиз небольшой, но несколько изменений JVM действительно заслуживают внимания:
🫡 Compact Object Headers теперь включены по умолчанию и уменьшают заголовок объекта с 12 до 8 байт;
🫡 G1 стал стандартным сборщиком мусора во всех окружениях;
🫡 TLS 1.3 получил гибридный постквантовый обмен ключами;
🫡 JFR автоматически скрывает пароли, токены, API-ключи и другие чувствительные значения;
🫡 Structured Concurrency дошла до седьмого Preview;
🫡 Vector API остался в Incubator и получил уже двенадцатую итерацию.

По сути, Java 27 стала ещё одним шагом подготовки платформы к следующему LTS-релизу.

⛓ Читать статью

Java Библиотека | Мы в Телеграм



⚠️ Я ДУМАЛ, ЧТО ЧТОБЫ БРАТЬ ЗАКАЗЫ НА РАЗРАБОТКУ ИЛИ ДИЗАЙН, НУЖНО УМЕТЬ ПРОГРАММИРОВАТЬ И БЫТЬ ПРОФЕССИНАЛЬНЫМ ДИЗАЙНЕРОМ

Писать код.
Разбираться в языках программирования.
Уметь работать в графических редакторах.
Знать композицию, типографику и дизайн.
Месяцами учиться.

👇 Оказалось, сейчас это уже не обязательно

Часть задач по разработке и дизайну можно объяснить нейросети обычными словами и собрать результат вместе с ней.

Причём речь про реальные задачи, за которые платят деньги:

➖ собрать презентацию и оформить слайды
➖ сделать визуалы и графику
➖ сделать Telegram-бота
➖ спарсить и обработать данные
➖ автоматизировать рутину
➖ собрать небольшой сервис

И вот это меняет правила игры.

Потому что раньше между:

«Я хочу освоить удалённую профессию»

и

«Я могу выполнить заказ»

могли стоять месяцы или годы обучения.

Сейчас Семён Красинцев решил показать весь процесс вживую.

На бесплатном эфире «Роботяга» он берёт реальный заказ с биржи фриланса и выполняет его нейронкой прямо при зрителях.

Заказ от 10 000 ₽.
На выполнение около 15 минут.

И самое интересное.

Семён не программист.

Он 20 лет занимается 3D-графикой и ставит задачи ИИ обычными словами.

На эфире покажет:

✅ как пройти путь от установки инструментов до выполнения заказов
✅ какие 5 ошибок чаще всего губят попытки самоучек
✅ где заканчиваются возможности бесплатных нейросетей
✅ как выполнять задачи без VPN и зарубежной карты
✅ какой инструмент можно использовать для старта

Причём инструмент тоже дадут бесплатно прямо на эфире.

Если давно смотрите на нейросети, но кажется:

«Я не технарь»
«Я не умею программировать»
«Я не дизайнер и не умею делать красивый визуал»
«Нужно сначала полгода учиться»

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

🎁 Участие бесплатное + дают курс по нейросетям для новичков.

👉 ЗАРЕГИСТРИРОВАТЬСЯ НА БЕСПЛАТНЫЙ ЭФИР И ПОСМОТРЕТЬ, КАК ИИ ВЫПОЛНЯЕТ ЗАКАЗ ЗА 15 МИНУТ
#реклама
О рекламодателе



Следим за появлением файлов в папке через WatchService!

Создадим наблюдатель и зарегистрируем каталог. Папка должна существовать заранее:
Path dir = Path.of("incoming");
try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY);

Константы ENTRY_CREATE и ENTRY_MODIFY берутся из StandardWatchEventKinds. try-with-resources закроет наблюдатель при выходе из блока.

Дальше ждём сигнал. Метод take блокирует поток, пока событие не появится:
while (true) {
WatchKey key = watcher.take();
for (WatchEvent<?> event : key.pollEvents()) {

Контекст события содержит имя относительно наблюдаемой папки, а не готовый абсолютный путь. Не подставляй его отдельно от dir:
if (event.kind() == OVERFLOW) continue;
Path file = dir.resolve((Path) event.context());
System.out.println(event.kind() + " " + file);
}

После обработки обязательно сбросим ключ. Если каталог удалён или больше недоступен, reset вернёт false:
if (!key.reset()) break;
}
}

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

Java Библиотека | Мы в Телеграм


Следим за появлением файлов в папке через WatchService!

Создадим наблюдатель и зарегистрируем каталог. Папка должна существовать заранее:
Path dir = Path.of("incoming");
try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY);

Константы ENTRY_CREATE и ENTRY_MODIFY берутся из StandardWatchEventKinds. try-with-resources закроет наблюдатель при выходе из блока.

Дальше ждём сигнал. Метод take блокирует поток, пока событие не появится:
while (true) {
WatchKey key = watcher.take();
for (WatchEvent<?> event : key.pollEvents()) {

Контекст события содержит имя относительно наблюдаемой папки, а не готовый абсолютный путь. Не подставляй его отдельно от dir:
if (event.kind() == OVERFLOW) continue;
Path file = dir.resolve((Path) event.context());
System.out.println(event.kind() + " " + file);
}

После обработки обязательно сбросим ключ. Если каталог удалён или больше недоступен, reset вернёт false:
if (!key.reset()) break;
}
}

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

Java Библиотека | Мы в Телеграм


Ленивая инициализация через Supplier — элегантная альтернатива double-checked locking

Когда нужно отложить создание тяжёлого объекта до первого обращения, многие вспоминают double-checked locking:

private volatile SomeHeavyObject obj;

public SomeHeavyObject getObj() {
if (obj == null) {
synchronized (this) {
if (obj == null) {
obj = new SomeHeavyObject();
}
}
}
return obj;
}

Многословно, хрупко, легко ошибиться. Есть лучше.

Современный подход — использовать Supplier с ленивой инициализацией:

private final Supplier<SomeHeavyObject> lazyObj = Suppliers.memoize(SomeHeavyObject::new);

public SomeHeavyObject getObj() {
return lazyObj.get();
}

Suppliers.memoize — из Guava. Он гарантирует потокобезопасную инициализацию один раз при первом вызове get().

Плюсы:
— Читается за секунду
— Потокобезопасно
— Нет дублирования кода
— Легко тестировать и заменять

Альтернатива в чистой Java: использовать AtomicReference и updateAndGet, но это уже длиннее и менее выразительно.

Если используешь Spring — можно просто обернуть бин в @Lazy. Но вне Spring, в обычных Java-приложениях или утилитах — Supplier с memoize() идеален.

Java Библиотека | Мы в Телеграм


Почему trim() может не убрать пробелы по краям строки?

В Java часто очищают пользовательский ввод так:
String name = input.trim();

Но trim() ориентируется на символы с кодом не выше U+0020. Некоторые пробельные символы Unicode под это правило не попадают.

Например, строка может начинаться и заканчиваться широким пробелом U+2003:
String input = "\u2003admin\u2003";
System.out.println(input.trim().length());

Видимые на экране отступы останутся. Это может сломать сравнение логина, поиск ключа или проверку поля формы.

Начиная с Java 11 есть strip(), который опирается на определение пробельного символа в Character.isWhitespace:
String name = input.strip();
System.out.println(name.equals("admin"));

Если нужна очистка только с одной стороны, есть отдельные методы:
String left = input.stripLeading();
String right = input.stripTrailing();

Для проверки строки, состоящей только из пробелов, пригодится isBlank():
if (input.isBlank()) {
throw new IllegalArgumentException("Empty name");
}

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


Java Библиотека | Мы в Телеграм


Java: не создавайте тяжёлые объекты раньше времени

Если объект дорогой в создании, но нужен только в редких сценариях, eager initialization может зря тратить время и память.

Вместо этого используйте lazy initialization:

private HeavyAnalyzer analyzer;

private HeavyAnalyzer analyzer() {
if (analyzer == null) {
analyzer = new HeavyAnalyzer();
}
return analyzer;
}

Так HeavyAnalyzer создастся только при первом реальном обращении.
Где особенно полезно:

- кэши;
- API-клиенты;
- тяжёлая конфигурация;
- анализаторы;
- объекты с дорогой инициализацией.
Для более чистого кода можно использовать Supplier, holder pattern или современные lazy-механизмы Java.

Если доступ идёт из нескольких потоков, простая проверка if (analyzer == null) уже недостаточна - нужна потокобезопасная реализация.

Java Библиотека | Мы в Телеграм


Во многих приложениях есть такой эндпоинт.

Фронтенд удаляет JWT после того, как пользователь нажимает «Выйти». Но вот вопрос… Действительно ли пользователь вышел из системы?

Что, если:
• JWT скопировали до выхода?
• Срок действия токена ещё не истёк?
• Другое устройство всё ещё использует тот же токен?

Будет ли ваш бэкенд по-прежнему принимать этот JWT? Как вы реализуете выход из системы в архитектуре со stateless JWT?

Java Библиотека | Мы в Телеграм


Почему одни ИИ-агенты помогают разработчикам экономить время, а другие требуют постоянной доработки? При создании агентных систем важно не только подключить LLM, но и правильно организовать логику работы агентов. Без единых подходов разработка замедляется: одни и те же решения приходится создавать заново, а поведение системы становится сложнее контролировать.

На открытом уроке 1 октября в 20:00 МСК разберем, как Agent Skills помогают ускорить создание ИИ-агентов, стандартизировать их логику и повторно использовать готовые решения. Вы узнаете, какие ИИ-инструменты помогают разработчикам быстрее собирать агентные системы и как применять эти подходы на практике.

Урок пройдет в преддверие старта курса «Разработка ИИ-агентов». Занятие подойдет разработчикам, ИИ-инженерам и техническим специалистам, которые создают агентные решения и хотят повысить скорость и качество разработки.

➡️Научитесь создавать более предсказуемые ИИ-агенты и тратить меньше времени на рутинную реализацию: https://vk.cc/d2a7Mw

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


Что такое механизм try-with-resources?

Механизм try-with-resources в Java — это конструкция, которая упрощает работу с ресурсами, требующими закрытия (например, файлы, сокеты, соединения с БД и т.д.). Он автоматически закрывает ресурсы после завершения блока try, избавляя от необходимости писать finally вручную.

Поддерживается с Java 7
Ресурсы должны реализовывать интерфейс
AutoCloseable

Пример:

try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// reader будет закрыт автоматически, даже если произойдёт исключение

Почему это важно:

* Уменьшает boilerplate-код
* Исключает утечки ресурсов
* Упрощает обработку исключений

Совет:

С Java 9 можно использовать уже объявленные переменные, если они final или effectively final:

BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
try (reader) {
System.out.println(reader.readLine());
}

Java Библиотека | Мы в Телеграм


Запускаем внешнюю команду через ProcessBuilder!

Иногда Java-приложению нужно вызвать системную утилиту, например git, ffmpeg или собственный скрипт деплоя. Склеивать такую команду в одну строку опасно, особенно если параметры приходят извне.

ProcessBuilder принимает аргументы отдельно. Так пробелы и специальные символы не превращаются в случайный shell-код.
var process = new ProcessBuilder(
"git", "status", "--short")
.redirectErrorStream(true)
.start();

redirectErrorStream(true) объединяет обычный вывод и ошибки. Это не даёт процессу зависнуть, когда буфер stderr заполняется, пока приложение читает только stdout.
try (var reader = process.inputReader()) {
var lines = reader.lines().toList();
lines.forEach(System.out::println);
}

После чтения обязательно дождитесь завершения и проверьте код возврата. Нулевой код обычно означает успех.
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IllegalStateException("Command failed");
}

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

Java Библиотека | Мы в Телеграм


🌞 Hibernate выполняет SQL не в том порядке, в котором ты его написал

Код читается сверху вниз, а Hibernate так не работает. При flush'е Hibernate складывает все действия в ActionQueue и выполняет их в строго фиксированном порядке по типу операции, а не по порядку вызова в коде:

1. orphan removal
2. INSERT
3. UPDATE
4. удаление коллекций
5. обновление коллекций
6. вставка коллекций
7. DELETE

Классический пример с unique-ограничением:

repo.delete(oldRow); // (1) в коде сначала удаляем
repo.save
(newRow); // (2) потом вставляем с тем же unique-значением
// commit -> ConstraintViolationException

На flush Hibernate переставит это в INSERT → DELETE. Вставка отработает до удаления, ловит конфликт по уникальному ключу, и падает, хотя в твоём коде последовательность была валидной.

🟡 Варианты решения:

— явный flush() между операциями, чтобы зафиксировать порядок;
— deferred (отложенные) constraints на стороне БД;
— пересмотр модели, чтобы коллизии в принципе не возникало.

Главное не считать, что порядок методов = порядок SQL. Это не так (для Hibernate).

Java Библиотека | Мы в Телеграм


👩‍💻 Как сделать Java-сервис устойчивее к сбоям

Resilience4j добавляет готовые паттерны CircuitBreaker, Retry, RateLimiter и Bulkhead без необходимости писать их с нуля.

Есть интеграции со Spring Boot, Reactor и Micrometer, а новая major-версия работает с Java 21+.

⛓ Ссылка на GitHub

Java Библиотека | Мы в Телеграм


Java: используйте Deque вместо Stack для работы по принципу LIFO («последним пришёл — первым вышел»).

Stack — устаревший класс, который наследуется от Vector и синхронизирует каждый вызов.

Deque предоставляет те же методы push, pop и peek.
Используйте ArrayDeque: он быстрее и рекомендуется начиная с Java 6.

Java Библиотека | Мы в Телеграм


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

Вот его основные особенности:

Независимость от платформы
Скомпилируйте код в байт-код один раз и запускайте его на любой машине с JVM.

Объектно-ориентированный подход
Всё строится вокруг классов и объектов, что упрощает организацию и сопровождение крупных приложений.

Надёжность
Автоматическая сборка мусора, строгая проверка типов и обработка исключений помогают избежать многих распространённых проблем во время выполнения.

Высокая производительность
JIT-компилятор (Just-In-Time) оптимизирует часто выполняемый код, обеспечивая скорость, близкую к нативной.

Многопоточность
Встроенные API для работы с конкурентностью упрощают параллельное выполнение нескольких задач.

Проверен на практике
Банковские системы, платформы электронной коммерции, Android-приложения и корпоративные бэкенды: Java лежит в основе критически важного ПО в самых разных отраслях.

Java Библиотека | Мы в Телеграм


Продвинутый Java-совет: используйте `ClassValue`

Если нужно кешировать метаданные отдельно для каждого класса, не обязательно создавать ConcurrentHashMap<Class<?>, Metadata>.

private static final ClassValue<Metadata> CACHE =
new ClassValue<>() {
@Override
protected Metadata computeValue(Class<?> type) {
return scan(type);
}
};

Metadata metadata = CACHE.get(MyClass.class);

ClassValue лениво вычисляет значение, безопасно работает при конкуренции потоков и хранит отдельный результат для каждого Class. Сбросить кеш можно через remove().

Полезно для reflection, сериализации, мапперов и собственных фреймворков.

Java Библиотека | Мы в Телеграм


⌨️ void и Void

В языке программирования Java ключевое слово void используется для указания, что метод не возвращает никакого значения. Метод с возвращаемым типом void выполняет определенные действия, но не производит результат, который можно было бы использовать в вызывающей части программы.

Для void еще есть класс обёртка Void. Класс Void — это класс-заполнитель который нельзя создать и который хранит ссылку на объект Class, представляющий ключевое слово Java void.

Этот класс можно использовать, например, в случаях, когда у вас есть обобщенный класс и вы хотите, чтобы метод ничего не возвращал (т.е. чтобы пользователь класса не ожидал ничего особенного оттуда получить):

interface I<T> {
T doStuff();
}

class A implements I<Void> {
@Override
public Void doStuff() {
// ничего другого вернуть нельзя
return null;
}
}

Или если в проекте регулярно используется Callable<T> для выполнения каких-то асинхронных задач и в тех ситуациях, когда результат работы задачи ничего не должен возвращать, можно реализовать Callable<Void>.

Java Библиотека | Мы в Телеграм



Как подружить строгий Java-энтерпрайз со всеми ИИ изменениями? Сбер поможет разобраться

24 сентября в московском офисе Сбера состоится Java Meetup — мероприятие, на котором эксперты расскажут:

✔️Что происходит в мире Java в 2026 году?
✔️В чём реальная польза (и скрытые риски) применения SDD, виртуальных потоков и Koog
✔️Обсудим, как меняется роль Java-разработчика под влиянием ИИ

Никакой теорий — только реальные кейсы от практиков!

Формат: офлайн в Москве (Кутузовский, 32) и онлайн трансляция.
Время: сбор гостей — 16:30, начало — 17:00.
Регистрация: здесь


Шифрование текста

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

Исходный текст и ключ (сдвиг):
String text = "hello world";
int shift = 3;

Создаём результат:
StringBuilder result = new StringBuilder();

Проходим по каждому символу:
for (char c : text.toCharArray()) {

Шифруем только буквы:
if (Character.isLetter(c)) {
char base = Character.isLowerCase(c) ? 'a' : 'A';
char enc = (char) ((c - base + shift) % 26 + base);
result.append(enc);
} else {
result.append(c);
}
}

Выводим зашифрованный текст:
System.out.println("Зашифрованный: " + result);

Обратное преобразование (расшифровка):
StringBuilder decoded = new StringBuilder();

for (char c : result.toString().toCharArray()) {
if (Character.isLetter(c)) {
char base = Character.isLowerCase(c) ? 'a' : 'A';
char dec = (char) ((c - base - shift + 26) % 26 + base);
decoded.append(dec);
} else {
decoded.append(c);
}
}

Вывод расшифрованного текста:
System.out.println("Расшифрованный: " + decoded);

После этого легко перейти к более сложным вещам: пароли, хеширование, реальные алгоритмы.

Java Библиотека | Мы в Телеграм

Показано 20 последних публикаций.