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());
// Значение автоматически