#java #null
#java #null
Вопрос:
Я отлаживаю приложение, которое разработано для изящной обработки случая foo
бытия null
. Однако при проверке журнала из рабочей среды NullPointerException
был выдан запрос на доступ к методу foo
после того, как он должен был быть корректно обработан.
это выглядит так
if (foo == null) {
throw new GracefulException();
}
Bar bar = foo.getBar();
Итак, с этим кодом на месте, NullPointerException
произошло в рабочей среде при вызове foo.getBar()
.
Мой вопрос просто таков: кто-нибудь когда-нибудь слышал о подобном странном поведении, происходящем или даже возможном? И если да, то что может быть причиной этого?
Комментарии:
1. Нет. Это невозможно. Мои деньги на плохо написанном
try ... catch
.2. Можете ли вы добавить полную трассировку стека?
3. @NilsH Код, который я опубликовал в своем вопросе, является сильно абстрактным примером фактического производственного кода. Трассировка стека была бы бесполезна, если бы я не значительно улучшил пример кода, чтобы отразить фактический код, и не выполнил работу по изменению номеров строк и удалению конфиденциальной информации из трассировки стека.
4. @NilsH Однако я подумаю о том, чтобы сделать это, если я не смогу это выяснить. Спасибо.
5. задействовано ли каким-либо образом параллельное выполнение?
Ответ №1:
Проверьте стек вызовов; это может исходить изнутри getBar()
.
Кроме того, если foo
это не локальная переменная, какой-нибудь другой поток может присвоить ей значение null после if
.
Комментарии:
1.
foo
это локальная переменная, но она получает свое значение из статического вспомогательного класса, который извлекает данные сеанса. Это веб-приложение, запущенное на сервере Tomcat. Я не уверен, могу ли я проверить стек вызовов изgetBar()
, потому что единственным доказательством того, что это вообще происходит, является файл журнала, который только сбрасывает трассировку стека необработанного исключения. Единственная причина, по которой я вообще знаю, откуда это происходитfoo.getBar()
, заключается в том, что это единственное, что происходит в номере строки, указанном в трассировке стека.2. @zero01alpha Если
foo
это локальная переменная, она не может быть обновлена между этими строками кода.3. Откуда могло что-то исходить
getBar()
, когда попытка вызоваgetBar()
— это то, что приводит кNullPointerException
? Простите мне недостаток знаний.4. @zero01alpha: Находится
getBar()
в верхней части трассировки стека? Кроме того, возможно ли, чтоgetBar()
было встроено?5.Метод, который вызывает
foo.getBar()
, находится в верхней части моей трассировки стека, и единственная причина, по которой я знаю,foo.getBar()
вызывает указатель null, из-за номера строки, включенного в трассировку стека.
Ответ №2:
Нет.
foo == null
Никогда не может бытьfalse
, еслиfoo
естьnull
.
Возможны следующие случаи:
- Ваше приложение многопоточное, а foo — это разделяемое поле, которое не синхронизировано должным образом. Если это так, то требуется синхронизация. Поскольку
foo
это локальная переменная, этот случай здесь неприменим. - Исключение исходит изнутри
foo.getBar()
. Этот случай можно легко идентифицировать по генерируемой трассировке стека. Проверьте, присутствует ли имя метода в трассировке стека.
Например, рассмотрим приведенный ниже код:
public class Temp {
public static void main(String[] args) {
new Temp().functionA();
}
void functionA() {
Foo foo = new Foo();
//foo = null; //Line 1
String str = foo.getBar();
}
}
class Foo {
Object x = new Object();
public String getBar() {
//x = null; //Line 2
return x.toString();
}
}
Если foo
это null
(строка 1 не прокомментирована), трассировка стека будет выглядеть как:
Exception in thread "main" java.lang.NullPointerException
at Temp.functionA(Temp.java:9)
at Temp.main(Temp.java:3)
Если x
это null
(строка 2 не прокомментирована), трассировка стека будет выглядеть как:
Exception in thread "main" java.lang.NullPointerException
at Foo.getBar(Temp.java:16)
at Temp.functionA(Temp.java:8)
at Temp.main(Temp.java:3)
Ответ №3:
Если foo.getBar()
упоминается в трассировке стека, исключение генерируется внутри этого метода, и вы ищете не в том месте.
Если вершина трассировки стека является методом, который содержит этот foo.getBar()
вызов, это foo
быть null
.
Поскольку вы пишете, что foo
это локальная переменная, это не связано с вмешательством какого-то другого потока — другой поток никогда не сможет установить значение вашей локальной переменной (максимум, что он может сделать, это изменить ее содержимое).
- Убедитесь, что между проверкой и вызовом метода нет инструкции, устанавливающей
foo
значениеnull
. - Убедитесь, что нет способа избежать проверки (т. Е. Что проверка не находится в условном блоке)
- Убедитесь, что между проверкой и вызовом метода нет перехвата блока catch
GracefulException
.