Перейти к основному содержимому

Полезные исключения NullPointerException в Java 14

· 4 мин. чтения

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 .