При каких обстоятельствах возможно, чтобы `foo == null` было false, когда `foo` действительно равно null?

#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 .

Возможны следующие случаи:

  1. Ваше приложение многопоточное, а foo — это разделяемое поле, которое не синхронизировано должным образом. Если это так, то требуется синхронизация. Поскольку foo это локальная переменная, этот случай здесь неприменим.
  2. Исключение исходит изнутри 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 .