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