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). При этом команда разработки демонстрирует взвешенный подход, не ломая обратную совместимость. Всё как всегда: шаг за шагом, без революций, но с постоянным улучшением уже знакомого и любимого инструмента.
Комментарии (16)

Lewigh
10.09.2025 10:43Пожалуй, одно из самых заметных изменений для каждого разработчика. Борьба с boilerplate вышла на новый уровень.
Наконец-то можно забыть про обязательные
public static void main(String[] args)для простых скриптов и утилит. Язык становится более доступным для новичков и приятным для опытных разработчиков, которые ценят лаконичность.void main() { IO.println("I'm still java. For now");}Просто революция не меньше. Теперь не нужно писать
public staticиString ...argsровно в одном месте программы. К черту неконсистентность языка ровно для одного места. К черту то, что Java настолько boilerplate что без сторонней утилиты (lombok) невозможно писать нормально. Главное что в методе main теперь на пару слов меньше писать. Борьба с boilerplate вышла на новый уровень однозначно.Официально прощаемся с 32-битными x86-системами.
При этом команда разработки демонстрирует взвешенный подход, не ломая обратную совместимость.
В одной и той же статье.

urvanov
10.09.2025 10:43Я, кстати, против многих новомодных изменений в Java, убирающих этот бойлерплейт. Первоначальная идея Java была противоположной, на мой взгляд.

Lewigh
10.09.2025 10:43Я, кстати, против многих новомодных изменений в Java, убирающих этот бойлерплейт. Первоначальная идея Java была противоположной, на мой взгляд.
Придерживаюсь аналогичного мнения.

DenSigma
10.09.2025 10:43Больше всего удар по бойлерплейт нанес record. С ним почти все фичи ломбока можно запретить.

urvanov
10.09.2025 10:43Реально серьёзных изменений для разработчиков будто бы и нет. Так причесали по мелочи. Будто бы не очень важный релиз, на мой взгляд.
В статье, кстати, не указано, но 25-ая версия должна быть с длительным сроком поддержки (LTS), судя по этой статье. Мне кажется, стоит указать.

AstarothAst
10.09.2025 10:43Пожалуй, одно из самых заметных изменений для каждого разработчикаВы издеваетесь? Каждому разработчику давно плевать где там в коде
public static void main(String[] args)- это одна единственная строка, которая генерируется за разработчика, и видит он ее примерно никогда. Изменение, ё-мое...
ValeryIvanov
10.09.2025 10:43Думаю это хороший подарок для тех кто обучают языку Java других людей. Объяснять новичку что это за
class Main public static void mainнемного затруднительно, ведь в процессе обучения до классов и методов доходят далеко не сразу.
polotenze
10.09.2025 10:43А мне кажется, наоборот. main это точка входа в программу, и надо понимать, откуда начинается выполнение, и как вообще работает java. А его отсуствие это просто некоторое удобство для написания скриптов

AstarothAst
10.09.2025 10:43Вот нет других проблем при изучении языка - сигнатура метода main все портит...

Lewigh
10.09.2025 10:43Думаю это хороший подарок для тех кто обучают языку Java других людей. Объяснять новичку что это за
class Main public static void mainнемного затруднительно, ведь в процессе обучения до классов и методов доходят далеко не сразу.Чуть позже очень удобно будет объяснять новичку:
// публичный статический метод void main() { System.out.println("Hello world"); }// приватный пакетный нестатический метод void hello() { System.out.println("Hello world"); }// снова публичный статический метод public static void hello() { System.out.println("Hello world"); }На месте новичка, с взорванным мозгом ,у меня была бы только одна мысль:
какой идиот это придумал, почему нельзя было сделать все в одном стиле?
supcheg Автор
10.09.2025 10:43В первом случае
#mainне является ни публичным, ни статическим.
Исходники:void main() { } void hello1() { } public static void hello2() { }Байт-код:
$ javap Sandbox Compiled from "Sandbox.java" final class Sandbox { Sandbox(); void main(); void hello1(); public static void hello2(); }

svn74
10.09.2025 10:43Неужели поддержка 32 битных jvm так уж накладно?... :( Тут же ж вся прелесть, - возможность писать под древние ПК...

x86chk
10.09.2025 10:43С учётом доминирования 64-битных платформ, что в мобильных устройствах, что на десктопе, это больше похоже на естественный процесс. Если брать целевые операционные системы, то Windows 10 — последняя ОС, существовавшая в 32-битной редакции, и её поддержка без учёта ESU и корпоративных изданий заканчивается уже через месяц. Из macOS 32-битную подсистему удалили несколько лет назад.
Не исключаю, что кто-то сделает новый JDK, где продолжат поддержку устаревшей архитектуры, всё зависит от реального спроса :)

pkokoshnikov
10.09.2025 10:43На самом деле в этом lts релизе много изменений по сравнению с предыдущим lts. Как будто не все понимают что этот релиз содержит фичи из 22, 23, 24 версии и теперь это lts.
mrprogre
Шикарная статья! Максимально понятно написано в отличие от других подобных.
Просто мысль: много народу на восьмой джаве пишет до сих пор и чаще закрывает подобные статьи, типа на потом. Было бы интересно почитать статьи именно от Вас со всеми важными различиями между восьмой и текущей. Это явно статей на 10 :)