1. Обзор
В этом руководстве мы продолжим нашу серию статей о Java 14 , взглянув на Helpful NullPointerException
, новую функцию, представленную в этой версии JDK.
2. Традиционные исключения NullPointerException
На практике мы часто видим или пишем код, который связывает методы в Java. Но когда этот код выдает исключение NullPointerException
, становится трудно понять, откуда возникает исключение.
Предположим, мы хотим узнать адрес электронной почты сотрудника:
String emailAddress = employee.getPersonalDetails().getEmailAddress().toLowerCase();
Если объект сотрудника ,
getPersonalDetails()
или getEmailAddress()
имеет значение null,
JVM генерирует исключение NullPointerException
:
Exception in thread "main" java.lang.NullPointerException
at com.foreach.java14.npe.HelpfulNullPointerException.main(HelpfulNullPointerException.java:10)
Какова основная причина исключения? Трудно определить, какая переменная имеет значение null
, не используя отладчик. Более того, JVM выведет только метод, имя файла и номер строки, вызвавшей исключение .
В следующем разделе мы рассмотрим, как Java 14 через JEP 358 решит эту проблему.
3. Полезные исключения NullPointerException
SAP внедрила Helpful NullPointerException
для своей коммерческой JVM в 2006 году. Это было предложено в качестве улучшения сообществу OpenJDK в феврале 2019 года, и вскоре после этого оно стало JEP. Следовательно, эта функция была завершена и выпущена в октябре 2019 года для выпуска JDK 14 .
По сути, JEP 358 направлен на улучшение читаемости исключений NullPointerException
, сгенерированных JVM, путем описания того, какая переменная имеет значение null
.
JEP 358 выводит подробное сообщение об исключении NullPointerException , описывая
нулевую
переменную, а также метод, имя файла и номер строки. Он работает, анализируя инструкции байт-кода программы. Следовательно, он способен точно определить, какая переменная или выражение имеет значение null
.
Самое главное, подробное сообщение об исключении по умолчанию отключено в JDK 14 . Чтобы включить его, нам нужно использовать параметр командной строки:
-XX:+ShowCodeDetailsInExceptionMessages
3.1. Подробное сообщение об исключении
Давайте рассмотрим повторный запуск кода с активированным флагом ShowCodeDetailsInExceptionMessages
:
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "String.toLowerCase()" because the return value of
"com.foreach.java14.npe.HelpfulNullPointerException$PersonalDetails.getEmailAddress()" is null
at com.foreach.java14.npe.HelpfulNullPointerException.main(HelpfulNullPointerException.java:10)
На этот раз из дополнительной информации мы знаем, что отсутствие адреса электронной почты в личных данных сотрудника вызывает наше исключение. Знания, полученные в результате этого усовершенствования, могут сэкономить нам время при отладке.
JVM составляет подробное сообщение об исключении из двух частей. Первая часть представляет неудачную операцию, следствие того, что ссылка имеет значение null
, а вторая часть определяет причину нулевой
ссылки :
Cannot invoke "String.toLowerCase()" because the return value of "getEmailAddress()" is null
Чтобы построить сообщение об исключении, JEP 358 воссоздает часть исходного кода, которая поместила нулевую
ссылку в стек операндов.
3.2. Технические аспекты
Теперь, когда у нас есть хорошее представление о том, как идентифицировать нулевые
ссылки с помощью Helpful NullPointerException
, давайте рассмотрим некоторые технические аспекты этого.
Во- первых, подробное вычисление сообщения выполняется только тогда, когда сама JVM выдает исключение NullPointerException
—
вычисление не будет выполнено, если мы явно выбрасываем исключение в нашем Java-коде. Причина этого в том, что в этих ситуациях, скорее всего, мы уже передаем значимое сообщение в конструктор исключений.
Во- вторых, JEP 358 вычисляет сообщение лениво, то есть только тогда, когда мы печатаем сообщение об исключении, а не когда возникает исключение . В результате не должно быть никакого влияния на производительность для обычных потоков JVM, где мы перехватываем и повторно выбрасываем исключения, поскольку мы не всегда печатаем сообщение об исключении.
Наконец, подробное сообщение об исключении может включать имена локальных переменных из нашего исходного кода . Таким образом, мы можем рассматривать это как потенциальную угрозу безопасности. Однако это происходит только тогда, когда мы запускаем код, скомпилированный с активированным флагом -g
, который создает и добавляет отладочную информацию в наш файл класса.
Рассмотрим простой пример, который мы скомпилировали, чтобы включить эту дополнительную информацию об отладке:
Employee employee = null;
employee.getName();
Когда мы запускаем этот код, сообщение об исключении печатает имя локальной переменной:
Cannot invoke
"com.foreach.java14.npe.HelpfulNullPointerException$Employee.getName()"
because "employee" is null
Напротив, без дополнительной отладочной информации JVM предоставляет только то, что ей известно о переменной в подробном сообщении:
Cannot invoke
"com.foreach.java14.npe.HelpfulNullPointerException$Employee.getName()"
because "<local1>" is null
Вместо имени локальной переменной ( employee
) JVM печатает индекс переменной, назначенный компилятором .
4. Вывод
В этом кратком руководстве мы узнали о полезных исключениях NullPointerException
в Java 14. Как показано выше, улучшенные сообщения помогают нам быстрее отлаживать код благодаря сведениям об исходном коде, представленным в сообщениях об исключениях.
Как всегда, полный исходный код статьи доступен на GitHub .