Java
16.7K subscribers
1.26K photos
74 videos
2 files
1.12K links
Самая актуальная информация по Java

По всем вопросам- @haarrp

@itchannels_telegram - 🔥лучшие каналы

@pythonl - 🐍

@ai_machinelearning_big_data- ml

@ArtificialIntelligencedl - AI

@datascienceiot - ds

@pythonlbooks 📚

РКН: clck.ru/3FmwKr

#VRHSZ
Download Telegram
🚀 HTTP-клиенты в Spring Boot через @HttpExchange

Описываете внешний API обычным Java-интерфейсом:

@HttpExchange("https://api.example.com")
public interface UserClient {

@GetExchange("/users/{id}")
User findById(@PathVariable Long id);

@PostExchange("/users")
User create(@RequestBody User user);
}

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

Таймауты, SSL и другие параметры настраиваются через spring.http.clients.*.

Меньше шаблонного кода, понятный контракт и удобное тестирование.

#SpringBoot4 #Java #HttpExchange
🔥13👍6❤2
💡 Java I/O tip

Нужно быстро сделать копию файла?

В Java для этого есть Files.copy():


Path src = Path.of("data.csv");
Path backup = Path.of("data.csv.bak");

Files.copy(src, backup, StandardCopyOption.REPLACE_EXISTING);


Что здесь важно:

* Path работает нормально на разных ОС
* REPLACE_EXISTING перезапишет файл, если backup уже есть
* удобно для бэкапов, экспорта и временных копий перед обработкой данных

Пример:


data.csv → data.csv.bak


Одна строка вместо ручного чтения и записи байтов.

#Java #Files
👍8❤1
🖥 Редкий Java-совет: не кэшируйте метаданные классов через `ConcurrentHashMap`

Для сериализаторов, DI-контейнеров и reflection-кода часто пишут так:


private static final Map<Class<?>, Metadata> CACHE =
new ConcurrentHashMap<>();

static Metadata metadata(Class<?> type) {
return CACHE.computeIfAbsent(type, Metadata::inspect);
}


Проблема: статическая Map хранит сильные ссылки на Class. В приложениях с плагинами, hot reload или несколькими ClassLoader такой кэш может мешать выгрузке старых классов.

Для данных, привязанных к конкретному классу, в Java есть ClassValue:


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

static Metadata metadata(Class<?> type) {
return METADATA.get(type);
}


Что это даёт:

- ленивое вычисление значения;
- потокобезопасную установку результата;
- отсутствие глобальной карты с Class в качестве сильных ключей;
- возможность сбросить значение через METADATA.remove(type).

Важный нюанс: computeValue() может выполниться несколько раз при гонке потоков, хотя сохранено будет одно значение. Поэтому внутри не должно быть необратимых побочных эффектов.

ClassValue особенно полезен для кэширования reflection-метаданных, MethodHandle, сериализаторов и обработчиков аннотаций.

#java #jvm #performance
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Java-совет: сравнивайте объекты без риска получить `NullPointerException`

Обычный вызов equals() опасен, если объект слева может быть null:


String a = null;
String b = "hi";

boolean result = a.equals(b); // NullPointerException


Безопаснее использовать Objects.equals():


boolean result = Objects.equals(a, b); // false


Метод корректно обрабатывает все случаи:


Objects.equals(null, null); // true
Objects.equals("hi", "hi"); // true
Objects.equals("hi", null); // false


Это особенно полезно внутри equals() у собственных классов:


@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User user)) return false;

return Objects.equals(id, user.id)
&& Objects.equals(email, user.email);
}


Правило простое: когда хотя бы одна сторона сравнения потенциально может быть null, используйте Objects.equals(a, b).

#Java #JavaTips #Programming
🔥8❤4👍1🎉1
💡 Java-совет: сравнивайте `Instant` с нужной точностью

Instant.equals() сравнивает время вплоть до наносекунд, поэтому даже небольшое различие даст false.


boolean sameMinute =
first.truncatedTo(ChronoUnit.MINUTES)
.equals(second.truncatedTo(ChronoUnit.MINUTES));


Так же можно сравнивать значения с точностью до часа или дня:


ChronoUnit.HOURS
ChronoUnit.DAYS


Важно: Instant работает в UTC. Для проверки одного календарного дня в конкретном часовом поясе сначала преобразуйте значения в ZonedDateTime.

#Java #JavaTime
👍8❤1🔥1
💡 Не используйте `Stream.peek()` для бизнес-логики

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


users.stream()
.peek(System.out::println)
.toList();


Для реальной обработки выбирайте:

* map() — преобразовать элементы;
* filter() — отфильтровать;
* forEach() — явно выполнить действие.

Код со скрытыми побочными эффектами сложнее читать, тестировать и поддерживать.

#Java #Streams
❤4👍3🥰1
💡 Spring Boot 4: найдите устаревшие настройки до того, как они сломают запуск

При миграции на Spring Boot 4 часть старых configuration properties была переименована или удалена. Проверять большие application.yml вручную необязательно — добавьте временный модуль:

Gradle:


runtimeOnly("org.springframework.boot:spring-boot-properties-migrator")


Maven:


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>


При запуске приложения migrator:

- найдёт устаревшие ключи;
- покажет новые названия в логах;
- временно подменит свойства во время выполнения.

Важный нюанс: модуль не переписывает конфигурационные файлы. Исправьте ключи вручную, снова запустите приложение и удалите зависимость.

properties-migrator — это временный помощник для миграции, а не библиотека, которую стоит оставлять в production.

#SpringBoot4 #Java #Migration
🔥14👍3❤1
💡 Java совет: два пути могут указывать на один и тот же файл

Path.equals() сравнивает только представление пути:


Path a = Path.of("data.txt");
Path b = Path.of("./data.txt");

System.out.println(a.equals(b)); // false


Хотя оба пути могут вести к одному файлу.

Для проверки используйте:


boolean same = Files.isSameFile(a, b);


Files.isSameFile() обращается к файловой системе и учитывает:

- относительные пути;
- символические ссылки;
- реальное расположение файла.


Path file = Path.of("data.txt");
Path link = Path.of("data-link.txt");

Files.isSameFile(file, link); // true, если link ведёт к data.txt


Полезно при поиске дубликатов, проверке пользовательских путей и защите от повторной обработки одного файла.

Учтите: метод может обращаться к диску и выбрасывать IOException, поэтому это не просто строковое сравнение.

Доступно начиная с Java 7.

#Java #JavaTips #Files
👍4
✔️ Spring Boot 4 убрал Undertow - при миграции это легко пропустить

Причина простая: Spring Boot 4 перешёл на Servlet 6.1, а Undertow с этой версией Servlet API несовместим. Поэтому из Boot 4 удалили и поддержку embedded Undertow, и spring-boot-starter-undertow.

Если раньше было:


<artifactId>spring-boot-starter-undertow</artifactId>


теперь используйте Tomcat по умолчанию или подключайте Jetty:


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>


Ещё один важный момент: не разворачивайте Spring Boot 4 в старом Servlet-контейнере — требуется совместимость с Servlet 6.1.

Перед миграцией Boot 3 → 4 обязательно проверьте серверную часть приложения.

#SpringBoot4 #Java #Servlet
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1🔥1
☕️ Java-совет: `LongAdder` вместо `AtomicLong` для горячих счётчиков

Частая ошибка в высоконагруженных системах:


private final AtomicLong requests = new AtomicLong();

requests.incrementAndGet();


Проблема: все потоки конкурируют за одну переменную.

Много потоков → больше CAS-коллизий → меньше производительность.

Для метрик лучше:


private final LongAdder requests = new LongAdder();

requests.increment();


Как работает:


AtomicLong:

Thread 1 ─┐
Thread 2 ─┼──> [100000]
Thread 3 ─┘


LongAdder:

Thread 1 → [10]
Thread 2 → [20]
Thread 3 → [15]

↓
sum()


LongAdder распределяет обновления между несколькими ячейками памяти, снижая конкуренцию.

Идеально для:

✅ Prometheus metrics
✅ счётчиков запросов
✅ статистики JVM
✅ rate limiter

Но важно:


adder.sum()


не гарантирует точный атомарный снимок.

Используйте:

- LongAdder → метрики и статистика;
- AtomicLong → когда нужно точное значение.

Одна замена класса может заметно улучшить производительность под нагрузкой.

#java #jvm #performance
❤8👍5🔥2
⚡️ Spring Boot 4: для интеграционных тестов REST лучше переходить с TestRestTemplate на RestTestClient.

Почему это удобнее:

- fluent API в стиле RestClient;
- одинаковый подход для тестов через MockMvc и реальный HTTP-порт;
- проще читать цепочки request → response → assertions;
- меньше привязки к старому TestRestTemplate.

Пример:


restTestClient.get()
.uri("/api/users/42")
.exchange()
.expectStatus().isOk()
.expectBody(User.class)
.value(user -> assertThat(user.id()).isEqualTo(42));


Если автоконфигурация не подтянулась автоматически, добавляем:


@AutoConfigureRestTestClient


Если начинаете новый проект на Boot 4, TestRestTemplate уже вряд ли стоит выбирать по привычке.

#SpringBoot4 #Java #RestTestClient #Testing
👍7❤1🔥1
💡 В Java можно избавиться от лишнего cast после instanceof.

Старый вариант:


if (animal instanceof Dog) {
Dog d = (Dog) animal;
d.bark();
}


С pattern matching переменная объявляется прямо в проверке:


if (animal instanceof Dog d) {
d.bark();
}


d существует только там, где условие instanceof гарантированно истинно. Меньше кода, меньше лишних приведений типов и проще читать.

#Java #PatternMatching
👍13❤5🔥4
☕ Java-баг, который выглядит нормально, но ломает `HashMap`

Что выведет этот код?


Map<List<Integer>, String> map = new HashMap<>();

List<Integer> key = new ArrayList<>(List.of(1, 2));

map.put(key, "Ritesh");

key.add(3);

System.out.println(map.get(key));


Многие ожидают:


Ritesh


Но результат:

```text
null
```

Почему?

HashMap ищет ключ через:

```
java
hashCode()
equals()
```

Когда мы сделали:

```
java
map.put(key, "Ritesh");
```

ключ был:

```text
[1, 2]
```

У него был один
hashCode.

Но потом:

```
java
key.add(3);
```

ключ стал:

```text
[1, 2, 3]
```

И его
hashCode изменился.

HashMap уже положил объект в bucket по старому hash, а при get() ищет по новому.

Схема:

```text
put()

[1,2]
hash = X
↓
bucket X


key.add(3)

[1,2,3]
hash = Y


get()

ищем bucket Y
→ запись не найдена
```

Главное правило:

❌ Не изменяйте объект после того, как использовали его как ключ в
HashMap.

Лучше использовать:

✅
String
✅
Integer
✅
record
✅ immutable-объекты

Например:

```
java
Map<List<Integer>, String> map = new HashMap<>();

List<Integer> key = List.of(1, 2);

map.put(key, "value");
```

Самые опасные баги в
Java - те, где объект всё ещё существует, ссылка всё та же, но структура данных уже не может его нормально найти.

#java #backend #programming
👍10🔥4🤔2😱1
💡 Java: не используй `Thread.sleep()` для ожидания работы

Thread.sleep() - это просто пауза на заданное время.

Проблема в том, что ты угадываешь:

❌ поток может проснуться слишком рано
❌ или наоборот, ждать дольше, чем нужно
❌ polling через sleep() создаёт лишние задержки и проверки

Для координации потоков лучше использовать:

✅ wait() / notify()
✅ CountDownLatch
✅ другие примитивы синхронизации из java.util.concurrent

Например:


CountDownLatch done = new CountDownLatch(1);

done.await(); // ждём сигнал
done.countDown(); // работа завершена


А если задача должна выполняться периодически, вместо бесконечного цикла с sleep() лучше взять



ScheduledExecutorService exec =
Executors.newSingleThreadScheduledExecutor();

exec.scheduleAtFixedRate(
this::doWork,
0,
1,
TimeUnit.SECONDS
);

Thread.sleep() хорошо подходит для простой задержки.


Для ожидания события и периодических задач в Java есть более точные инструменты.

#Java #Concurrency
❤6👍6🔥4
💡 Java: переопределяешь `equals()` - переопредели и `hashCode()`

Два объекта с одинаковым email могут быть равны по equals(), но HashSet.contains() при этом вернёт false.

Причина: HashSet и HashMap сначала используют хеш, чтобы определить, где искать объект, а затем проверяют равенство. Если хеши различаются, до equals() дело может не дойти.

Правило: равные объекты обязаны иметь одинаковый `hashCode()`. При этом одинаковый хеш не гарантирует равенство - коллизии допустимы.

В примере на картинке равенство определяется по email, поэтому хеш тоже вычисляется по нему.

И ещё: не меняйте поля, участвующие в `equals()` и `hashCode()`, пока объект хранится в `HashSet` или используется как ключ `HashMap` - поиск может перестать его находить.

#Java #OOP
👍5❤3🎉1
🍃 Spring Boot 4: проверьте зависимости Flyway и Liquibase

После обновления одной зависимости flyway-core или liquibase-core недостаточно для автоконфигурации миграций. В Spring Boot 4 поддержку технологий вынесли в отдельные модули.

Используйте соответствующий стартер:

* Flyway → spring-boot-starter-flyway
* Liquibase → spring-boot-starter-liquibase

Для тестов, включая test slices:

* spring-boot-starter-flyway-test
* spring-boot-starter-liquibase-test

Все перечисленные зависимости относятся к группе org.springframework.boot. Тестовые стартеры добавляются с областью test в Maven или через testImplementation в Gradle.

Руководство по миграции: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide#module-dependencies

#SpringBoot #Java #Flyway
👍6🔥3❤1
💡 Java: Checked vs Unchecked Exceptions, коротко и по делу

В Java исключения делятся на две основные категории:

- Checked - например, IOException. Компилятор заставит либо обработать исключение через try/catch, либо указать throws.
- Unchecked — наследники RuntimeException. Обязательной обработки на этапе компиляции нет.

Пример:

IOException → checked
NullPointerException → unchecked
IllegalArgumentException → unchecked

Практическое правило: checked exceptions имеет смысл использовать для ситуаций, из которых вызывающий код действительно может восстановиться.

#Java #ErrorHandling
👍4❤1
🚀 Spring Boot 4 получил отдельный starter для RestClient

Теперь imperative HTTP-клиент можно подключать через:

spring-boot-starter-restclient

Для реактивного WebClient используется:

spring-boot-starter-webclient

А настройки HTTP-клиентов собраны под единым префиксом:

spring.http.clients.*

То есть конфигурация стала понятнее, а выбор между imperative и reactive подходом — более явным.

#SpringBoot4 #RestClient #Java
❤7👍4🔥4🎉1
⚡️Продвинутый 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 #JavaTips #Backend
🔥7❤3🎉3👍1
🍃 Не раскладывайте весь Spring-проект по папкам controller, service и repository.

В новом This Week in Spring Джош Лонг советует группировать код по функциональности, используя подход Spring Modulith. Например, orders, payments, users, внутри которых находятся относящиеся к ним слои.

Ещё в подборке:

• JobRunr 9: поиск упавшего шага и задач, вызывающих замедление, остановка выполнения без потери работы.
• Spring AI: разбор агентных паттернов и вызова инструментов от руководителя проекта Кристиана Цолова.
• Progen CLI: видео о генерации каркаса приложения.
• IntelliJ IDEA: обзор новых возможностей.
• A Bootiful Podcast: разговор с создателем Jenkins Косуке Кавагути.

https://spring.io/blog/2026/10/13/this-week-in-spring-october-13th-2026

#Java #SpringBoot
🔥7❤5🎉2