Java в 2025 году: новые возможности и практические примеры

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

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

Начать курс

Java в 2025 году: что нового? Обзор на практике

Java продолжает активно развиваться, и 2025 год не стал исключением. Релизы JDK 23 и 24 принесли долгожданные стабилизации экспериментальных функций, а также несколько совершенно новых возможностей, которые кардинально меняют подход к написанию кода. В этой статье мы разберем ключевые нововведения на практических примерах, чтобы вы могли сразу применить их в своих проектах. Мы рассмотрим виртуальные потоки (Project Loom), улучшенный Pattern Matching, новые возможности Record и Sealed Classes, а также изменения в API.

1. Виртуальные потоки (Project Loom) — стандарт де-факто

Виртуальные потоки, ставшие стабильными в JDK 21, в 2025 году превратились в основной инструмент для высоконагруженных серверных приложений. Они позволяют создавать миллионы легковесных потоков, не нагружая операционную систему. Главное преимущество — простота: код выглядит как синхронный, но работает асинхронно.

Пример: Высоконагруженный HTTP-клиент

Предположим, вам нужно отправить 10 000 HTTP-запросов. С классическими потоками это вызовет OutOfMemoryError или огромные накладные расходы. С виртуальными потоками задача решается элегантно:

import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class VirtualThreadsExample {
    public static void main(String[] args) throws Exception {
        var client = HttpClient.newHttpClient();

        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            IntStream.range(0, 10_000).forEach(i -> {
                executor.submit(() -> {
                    var request = HttpRequest.newBuilder()
                            .uri(URI.create("https://httpbin.org/delay/1"))
                            .build();
                    try {
                        var response = client.send(request, HttpResponse.BodyHandlers.ofString());
                        System.out.println("Запрос " + i + " завершен, статус: " + response.statusCode());
                    } catch (Exception e) {
                        System.err.println("Ошибка: " + e.getMessage());
                    }
                });
            });
        } // executor.close() ожидает завершения всех потоков
    }
}

Что изменилось в 2025? Улучшена интеграция с сокетами и базами данных. Теперь виртуальные потоки корректно работают с большинством популярных драйверов JDBC (например, PostgreSQL) без необходимости их модификации. Также появилась возможность задавать пользовательские планировщики (ForkJoinPool) для тонкой настройки производительности.

2. Pattern Matching — финальная версия и новые шаблоны

Pattern Matching для switch и instanceof в 2025 году достиг полной зрелости. Главное новшество — поддержка record patterns и guarded patterns (условные шаблоны). Это позволяет писать очень лаконичный и безопасный код для разбора сложных структур данных.

Пример: Разбор JSON-подобной структуры

sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}

public class PatternMatchingExample {
    public static double calculateArea(Shape shape) {
        return switch (shape) {
            case Circle c -> Math.PI * c.radius() * c.radius();
            case Rectangle r -> r.width() * r.height();
            // Guarded pattern: проверяем, что треугольник не вырожденный
            case Triangle t when t.base() > 0 && t.height() > 0 -> 0.5 * t.base() * t.height();
            case Triangle t -> {
                System.err.println("Треугольник с нулевыми сторонами!");
                yield 0;
            }
            // Default больше не нужен, так как все случаи покрыты
        };
    }

    public static void main(String[] args) {
        System.out.println(calculateArea(new Circle(5))); // 78.5398...
        System.out.println(calculateArea(new Triangle(0, 5))); // 0 (с предупреждением)
    }
}

Record Patterns в действии:

record Point(int x, int y) {}
record Line(Point start, Point end) {}

public static void printLineInfo(Object obj) {
    if (obj instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
        System.out.println("Линия от (" + x1 + "," + y1 + ") до (" + x2 + "," + y2 + ")");
    }
}

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

3. Улучшения в работе с памятью и новое API

В 2025 году Java представила несколько важных API, упрощающих работу с памятью и данными.

3.1. Vector API (стабилизация)

Vector API позволяет выполнять SIMD-операции (одна инструкция над множеством данных) без написания нативного кода. Это дает прирост производительности в научных расчетах, обработке изображений и машинном обучении.

import jdk.incubator.vector.*;

public class VectorExample {
    public static void main(String[] args) {
        var species = FloatVector.SPECIES_256; // 256-битный вектор (8 float'ов)
        float[] a = {1, 2, 3, 4, 5, 6, 7, 8};
        float[] b = {8, 7, 6, 5, 4, 3, 2, 1};
        float[] c = new float[8];

        FloatVector va = FloatVector.fromArray(species, a, 0);
        FloatVector vb = FloatVector.fromArray(species, b, 0);
        FloatVector vc = va.add(vb); // Покомпонентное сложение
        vc.intoArray(c, 0);

        // c = [9, 9, 9, 9, 9, 9, 9, 9]
        System.out.println(java.util.Arrays.toString(c));
    }
}

3.2. Scoped Values (замена ThreadLocal)

Scoped Values — это более безопасная и производительная альтернатива ThreadLocal. Они особенно полезны в сочетании с виртуальными потоками, где ThreadLocal может приводить к утечкам памяти.

public class ScopedValuesExample {
    private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();

    public static void main(String[] args) {
        ScopedValue.where(USER_ID, "user-42")
                   .run(() -> processRequest());
    }

    static void processRequest() {
        // Значение доступно только внутри этого метода и его вызовов
        System.out.println("Обрабатываю запрос для: " + USER_ID.get());
        // Значение автоматически                

Блоги

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