Как использовать новые возможности Java 17
Рассказываем, как использовать обновления Java 17, чтобы повысить производительность и безопасность приложений.
5К открытий7К показов
Java 17 была выпущена почти 2 года назад, в сентябре 2021 года, но, возможно, именно к этой версии популярного языка программирования стоит присмотреться, если вы начинаете новый проект или желаете обновить версию Java, чтобы использовать улучшенные возможности.
Java 17 является версией с долгосрочной поддержкой (long-term support), а это значит, что она будет получать обновления, в том числе, связанные с безопасностью, в течение длительного времени. Проще говоря, это версия, которая будет использоваться в индустрии на протяжении, как минимум, нескольких лет – так, как сейчас используется предыдущая версия с долгосрочной поддержкой Java 11.
Если Вы хотите использовать новейшие версии фреймворков Spring 6 и Spring Boot 3, Вам также понадобится версия Java не ниже 17
Что было улучшено в Java 17
Java 17 содержит множество улучшений, но мы сосредоточимся именно на тех, которые могут повысить производительность и безопасность приложений. Если для использования новых средств и возможностей языка нужно менять код, для получения прироста производительности достаточно просто обновить версию JDK.
Производительность
Java 17 быстрее Java 11 на 6-16% в зависимости от используемого сборщика мусора:
- На 8.66% быстрее для сборщика мусора по умолчанию (G1GC – default garbage collector)
- На 6.54% быстрее для параллельного сборщика мусора (ParallelGC – Parallel Garbage Collector)
Java 17 также несколько быстрее, чем Java 16.
Безопасность
В плане безопасности изменения произведены в нескольких областях:
1. Были удалены части кода, которые уже были помечены как deprecated, или же были экспериментальными и не получили широкого применения:
- Applet API
- RMI Activation
- Security Manager
- Экспериментальные компиляторы AOT и JIT
2. Был выключен переключатель командной строки, который позволял ослаблять строгую инкапсуляцию
3. Были добавлены новые особенности языка программирования:
- Запечатанные (закрытые) классы – sealed classes
- Контекстно-специфичные фильтры десериализации
Остановимся на новых возможностях языка Java 17 подробнее.
Запечатанные классы и интерфейсы
Запечатанный класс позволяет ограничивать или выбирать его подклассы. Класс не может расширять запечатанный класс, если его нет в списке разрешенных дочерних классов родительского класса. Основной мотив здесь – иметь возможность для суперкласса быть широко доступным, но не широко расширяемым.
Класс запечатывается с использованием ключевого слова sealed. За запечатанным классом должно следовать ключевое слово permits вместе со списком классов, которые могут его расширить. Аналогичным образом можно создавать и запечатанные интерфейсы. Вот простой пример запечатанного класса:
public abstract sealed class Device permits Laptop, Smartphone {...}
Класс, который его расширяет, должен иметь один из модификаторов:
- final – класс не может быть больше расширен
- sealed – нам нужно указать классы, которые могут его расширять, посредством модификатора permits
- non-sealed – класс снова становится доступным для стандартного расширения
Пример:
public non-sealed class Laptop extends Device {...}
Контекстно-специфичные фильтры десериализации
Сериализация — это механизм преобразования состояния объекта в памяти в поток байтов и сохранение объекта в файл. Простой пример сериализации:
Laptop laptop = new Laptop();String filename = "laptop.ser";FileOutputStream file = new FileOutputStream(filename);ObjectOutputStream out = new ObjectOutputStream(file);out.writeObject(laptop);out.close();file.close();
Десериализация — это обратный процесс, при котором поток байтов используется для воссоздания фактического объекта Java в памяти. Пример:
String filename = "laptop.ser";FileInputStream file = new FileInputStream(filename);ObjectInputStream in = new ObjectInputStream(file);Laptop laptop = (Laptop)in.readObject();in.close();file.close();
Объект Java является сериализуемым, если его класс или любой из его суперклассов реализует либо интерфейс java.io.Serializable, либо субинтерфейс java.io.Externalizable.
Десериализация ненадежных данных может привести к уязвимостям, позволяющим злоумышленнику выполнить произвольный код.
Laptop laptop = (Laptop)in.readObject();
Метод readObject в java.io.ObjectInputStream создаст сериализуемый объект класса Laptop. Если созданный объект имеет какие-либо уязвимые данные, например, удаленное выполнение кода, то это может вызвать проблему безопасности в системе.
Чтобы решить эту проблему, были представлены фильтры десериализации – ещё в Java 9. Они позволяют проверять поток данных, прежде чем его десериализировать. Это можно сделать статически или динамически через ObjectInputFilter API.
FileInputStream file = new FileInputStream(filename);ObjectInputStream in = new ObjectInputStream(file);ObjectInputFilter filesOnlyFilter = ObjectInputFilter.Config.createFilter("!com.example.Laptop;");in.setObjectInputFilter(filesOnlyFilter);Laptop laptop = (Laptop)in.readObject();
Java 17 добавляет больше гибкости фильтрам десериализации, теперь появилась возможность использовать FilterFactory. Следующий код использует слияние существующего и нового фильтров:
ObjectInputFilter.Config.setSerialFilterFactory((filter1, filter2) -> ObjectInputFilter.merge(filter2,filter1));
Также появилась возможность создавать фильтры в удобочитаемом коде посредством методов allowFilter() and rejectFilter() в ObjectInputFilter.
Итог
Если Вы планируете обновить версию Java для улучшения производительности и повышения безопасности, а также для того, чтобы применять новые возможности языка, Java 17 выглядит подходящим кандидатом, так как это версия с долгосрочной поддержкой, которая быстрее и безопаснее всех предыдущих.
Об авторе
Сергей Стариков (Siarhei Starykau), Solution Architect в компании EPAM Systems, работаю с языком программирования Java 18 лет.
5К открытий7К показов