Java в 2025: что изменится и почему пора обновлять код

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Java не умирает — она перерождается

Каждый год кто-то пишет «Java мертва». И каждый год Java доказывает обратное. В 2025 году язык, которому уже почти 30 лет, получает сразу несколько фундаментальных обновлений. Если вы пишете на Java, но последний раз смотрели changelog год назад — вы уже отстали. Давайте разберем, что конкретно изменится и как эти изменения повлияют на ваш код.

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

Виртуальные потоки (Project Loom) — конец эпохи блокировок

Это, пожалуй, самое громкое изменение. Виртуальные потоки (virtual threads) — это легковесные потоки, которые позволяют писать асинхронный код в синхронном стиле. В 2025 году они наконец-то становятся стабильными и рекомендуемыми для продакшена.

Раньше вы писали что-то вроде:

ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> {
    // блокирующий вызов
    String result = restTemplate.getForObject(url, String.class);
    return result;
});

Проблема в том, что каждый поток — это тяжелый объект. 10 потоков — ок, 10 000 — уже проблемы. С виртуальными потоками вы можете запускать миллионы потоков без оглядки на память:

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 100_000; i++) {
        executor.submit(() -> {
            // теперь это легковесно
            String result = restTemplate.getForObject(url, String.class);
            return result;
        });
    }
}

Ключевое отличие: виртуальные потоки не привязаны к потокам ОС. Когда поток блокируется (например, на I/O), JVM просто переключает его на другой виртуальный поток. Никаких накладных расходов.

Что это значит для вас? Забудьте про сложные асинхронные фреймворки типа CompletableFuture и реактивные библиотеки. В 2025 году стандартный синхронный код на виртуальных потоках будет и быстрее, и проще.

Как мигрировать

Замените newFixedThreadPool на newVirtualThreadPerTaskExecutor. Если используете Spring Boot — обновитесь до версии 3.2+, там уже есть поддержка. Только убедитесь, что ваши библиотеки не используют synchronized в критических секциях — это единственный момент, где виртуальные потоки могут «застрять».

Pattern Matching и Switch — код становится короче

В 2025 году pattern matching добрался до switch и instanceof. Теперь вместо кучи вложенных if-else можно писать лаконично и читаемо.

Было (старый стиль):

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 * 2);
}

Стало (Java 21+):

switch (obj) {
    case String s -> System.out.println(s.length());
    case Integer i -> System.out.println(i * 2);
    case null -> System.out.println("null");
    default -> System.out.println("unknown");
}

Обратите внимание на case null — это отдельное улучшение. Раньше switch выбрасывал NullPointerException при передаче null. Теперь можно обработать это явно.

Еще крутая фича — record patterns. Если у вас есть record:

record Point(int x, int y) {}

То можно писать:

if (obj instanceof Point(int x, int y)) {
    System.out.println("x=" + x + ", y=" + y);
}

Без лишнего приведения типов. В 2025 году это будет мейнстримом — используйте, чтобы код стал чище.

Сборка мусора: ZGC и Generational GC

В 2025 году сборка мусора стала еще быстрее. ZGC (Z Garbage Collector) теперь включен по умолчанию для большинства приложений. Его главное преимущество — паузы менее 1 миллисекунды, независимо от размера кучи.

Раньше вы могли испытывать «фризы» приложения на несколько секунд во время GC. С ZGC это в прошлом. Для веб-серверов и микросервисов это означает стабильный latency без тюнинга.

Но есть нюанс: ZGC потребляет больше CPU. Если ваше приложение не чувствительно к задержкам (например, batch-обработка), можно оставить G1GC. Но для real-time систем — ZGC ваш выбор.

Настройка простая:

-XX:+UseZGC

Или, если используете Java 22+:

-XX:+UseZGC -XX:ZCollectionInterval=60

Также появился Generational ZGC — гибрид, который разделяет молодое и старое поколения. Он чуть сложнее, но дает лучшую производительность для приложений с короткоживущими объектами.

Foreign Function & Memory API — прощайте, JNI

В 2025 году Foreign Function & Memory API (FFM) становится стабильным. Это замена старому и неудобному JNI (Java Native Interface). Теперь вызывать C-библиотеки можно без нативного кода.

Пример: вызов функции printf из libc:

import java.lang.foreign.*;
import java.lang.invoke.*;

public class HelloNative {
    public static void main(String[] args) throws Throwable {
        Linker linker = Linker.nativeLinker();
        SymbolLookup stdlib = linker.defaultLookup();
        MethodHandle printf = linker.downcallHandle(
            stdlib.find("printf").get(),
            FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS)
        );
        try (Arena arena = Arena.ofConfined()) {
            MemorySegment str = arena.allocateUtf8String("Hello from Java 2025!\n");
            printf.invoke(str);
        }
    }
}

Да, выглядит громоздко, но это в разы проще, чем писать .h файлы и компилировать JNI. Для работы с си-библиотеками (например, для криптографии или работы с оборудованием) это спасение.

Совет: не используйте FFM для простых задач — для них хватит стандартной библиотеки. Но если вам нужно вызвать нативную функцию, теперь это делается без боли.

String Templates — строки станут умнее

В 2025 году в Java появляются string templates (в预览 версии с Java 21, стабильные в 23+). Это как f-strings в Python, только типобезопасные.

Было:

String message = "User " + user.getName() + " has " + user.getPoints() + " points";

Стало:

String message = STR."User \{user.getName()} has \{user.getPoints()} points";

Префикс STR — это процессор шаблонов. Можно создавать свои процессоры для безопасной интерполяции SQL, JSON и т.д. Например:

<

Блоги

Рекомендаций по книгам