Java не стоит на месте и продолжает активно развиваться. Скоро выйдет уже 25-я версия языка. В этом релизе изменили работу с boilerplate-кодом и конструкторами, а также отказались от поддержки устаревших систем. Обо всех этих и других нововведениях расскажем в статье.

Новые API
JEP 506: Scoped Values
Как часто вы использовали ThreadLocal
? Даже не так: слышали ли вы о нём? Если коротко, ThreadLocal
— это контейнер, хранящий уникальное значение переменной для каждого потока.
С ходу тут и не скажешь, какие проблемы могут возникнуть. Но они есть и довольно серьёзные:
неограниченное время жизни;
неконтролируемая изменяемость;
затратное (по производительности) наследование значения дочерними потоками.
В этом релизе появился новый механизм ScopedValue
, решающий перечисленные проблемы.
Пример:
ScopedValue<FrameworkContext> CONTEXT = ScopedValue.newInstance();
void serve(Request request, Response response) {
var context = createContext(request);
ScopedValue.where(CONTEXT, context)
.run(() -> handle(request, response));
}
void handle(Request request, Response response) {
// ....
readKey(key);
// ....
}
Key readKey(String key) {
var context = CONTEXT.get();
var connection = getConnection(context);
return connection.readKey(key);
}
Здесь с помощью ScopedValue#where
настраивается окружение, а #run
запускает Application#handle
с этим контекстом. Сразу после выхода из метода контекст чистится.
JEP 510: Key Derivation Function API
Теперь в Java есть единый встроенный способ для работы с KDF-алгоритмами.
KDF-алгоритмы — алгоритмы, безопасно генерирующие криптографические ключи из исходных секретов (паролей/ключей). Более подробно тут.
Раньше для разных алгоритмов требовались разные подходы, теперь всё просто:
KDF hkdf = KDF.getInstance("HKDF-SHA256");
AlgorithmParameterSpec params =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial)
.addSalt(salt)
.thenExpand(info, 32);
SecretKey key = hkdf.deriveKey("AES", params);
Все танцы с бубном с SecretKeyFactory
и PBEKeySpec
для PBKDF2 или возня с HKDFParameterSpec
теперь в прошлом. Один API, чтобы управлять всеми.
Нововведения в синтаксисе
JEP 511: Module Import Declarations
Настоящий подарок для тех, кто устал от бесконечных списков импортов. Теперь можно импортировать весь модуль одной строкой. Особенно элегантно это решение смотрится в паре с JEP 512, позволяя создавать лаконичные, почти скриптовые сценарии, без потери строгости типов.
Вот как это выглядит в комбинации с компактными main
-методами:
import module org.slf4j;
Logger log = LoggerFactory.getLogger("MyAppLogger");
void main() {
Stream<String> stringStream = ThreadLocalRandom.current().longs(10)
.mapToObj(String::valueOf);
for (var string : (Iterable<String>) (stringStream::iterator)) {
log.info(string);
}
}
JEP 512: Compact Source Files and Instance Main Methods
Пожалуй, одно из самых заметных изменений для каждого разработчика. Борьба с boilerplate вышла на новый уровень.
Наконец-то можно забыть про обязательные public static void main(String[] args)
для простых скриптов и утилит. Язык становится более доступным для новичков и приятным для опытных разработчиков, которые ценят лаконичность. Под капотом это работает за счёт неявного объявления класса и автоматического импорта модуля java.base
. А для ещё большего удобства в java.lang
появился новый класс IO
с базовыми методами ввода-вывода.
Boilerplate-код — это повторяющийся шаблонный код, который приходится писать вручную для решения стандартных задач, но при этом он несёт мало уникальной логики.
Достаточно лишь два раза в день...
void main() {
IO.println("I'm still java. For now");
}
... и голова болеть не будет.
JEP 513: Flexible Constructor Bodies
Это то, о чём многие мечтали годами. Возможность писать код до вызова super()
или this()
— это не просто синтаксический сахар, а фундаментальное упрощение логики инициализации объектов. После preview в Java 22 фича доработана и позволяет не только валидировать аргументы, но и безопасно инициализировать поля, решая классические проблемы с порядком выполнения кода, которые находила одна из диагностик PVS-Studio :)
Не будем указывать пальцем, но это всегда было ограничение языка, а не байт-кода, JVM или чего-то ещё.
Sandbox(String key) {
if (!key.startsWith("sandbox:"))
throw new IllegalArgumentException(key);
super(key);
}
Изменения в платформе Java
JEP 503: Remove the 32-bit x86 Port
Официально прощаемся с 32-битными x86-системами.
Напомним: в прошлом релизе отправили в утиль поддержку 32-битной Windows, а теперь добили и остальные платформы (Linux x86, macOS). Причина проста: поддержка древнего железа требует немалых усилий, а пользователей таких систем совсем-совсем мало.
Если вы ещё используете 32-битную JVM (вау!), пора задуматься об обновлении.
JEP 519: Compact Object Headers
Сжать нельзя выделить.
Каждый объект в Java-куче имеет скрытый заголовок — как паспорт с метаданными. До Java 25 в 64-битных JVM он весил 12 байт. Теперь его можно ужать до 8 байт флагом -XX:+UseCompactObjectHeaders
.
JEP 521: Generational Shenandoah
Shenandoah — низкопаузный параллельный сборщик мусора, работающий конкурентно с приложением. Основная задача: минимизировать stop-the-world паузы (<10 мс) даже на терабайтных хипах. Вместо привычных поколений использует регионы фиксированного размера.
Теперь Shenandoah официально поддерживает поколения (ранее это было экспериментальной фичей): регионы могут быть молодыми или старыми. Зачем?
снижение потребление памяти без увеличения пауз GC;
уменьшение энергопотребления и нагрузки на CPU.
Ahead-of-Time оптимизации
JEP 514: Ahead-of-Time Command-Line Ergonomics
Java понемногу старается упростить себя. В том числе и CLI. Теперь для создания новомодного AOT-кэша нужен только один флаг: -XX:AOTCacheOutput=<file>
.
Подробнее о Ahead-of-Time кэше можете прочитать в нашей предыдущей статье о нововведениях в Java.
JEP 515: Ahead-of-Time Method Profiling
AOT-кэш развивается. В этом релизе в AOT добавили возможность сохранять профили выполнения методов. Теперь JVM может применить оптимизации JIT сразу, используя кэш, не дожидаясь сбора статистики выполнения. Соответственно, приложения в продакшене быстрее запускаются и достигают пиковой производительности.
Улучшения JFR
JEP 518: JFR Cooperative Sampling
Профилирование производительности становится точнее! Раньше JFR использовал прерывания потоков для сбора данных, что могло искажать реальную картину при высокой нагрузке.
Теперь же реализована кооперативная выборка: потоки приостанавливаются в безопасных точках (safepoints), чтобы отдать статистику. Данные остаются консистентными, а влияние на производительность минимально.
JEP 520: JFR Method Timing & Tracing
Хотите не только знать, где тормозит код, но и почему? Новые события в JFR дают ответ:
MethodTiming
фиксирует время выполнения методов с наносекундной точностью;MethodTrace
строит цепочки вызовов для критичных участков.
In Preview
В качестве предварительного просмотра в релиз включили следующие JEP'ы:
Заключение
Полный список JEP можно найти на сайте OpenJDK.
Эволюция Java продолжается, и 25-й релиз отличное тому доказательство. Язык целенаправленно движется в сторону увеличения производительности (AOT, Shenandoah), уменьшения boilerplate (компактный синтаксис) и усиления безопасности (KDF). При этом команда разработки демонстрирует взвешенный подход, не ломая обратную совместимость. Всё как всегда: шаг за шагом, без революций, но с постоянным улучшением уже знакомого и любимого инструмента.
mrprogre
Шикарная статья! Максимально понятно написано в отличие от других подобных.
Просто мысль: много народу на восьмой джаве пишет до сих пор и чаще закрывает подобные статьи, типа на потом. Было бы интересно почитать статьи именно от Вас со всеми важными различиями между восьмой и текущей. Это явно статей на 10 :)