Почему Java не говорит вам, какой указатель имеет значение null?
Я всегда задавался вопросом, почему JVM не говорит вам, какой указатель (или, точнее, какая переменная) является нулевым при вызове NullPointerException
.
Номер строки не является достаточно конкретным, потому что строка нарушения часто может содержать множество переменных, которые могли бы вызвать ошибку.
Есть ли какой-либо флаг компилятора или JVM, который сделает эти сообщения исключений более полезными?
Ответы
Ответ 1
Это потому, что разыменование всегда происходит, когда нет имени. Значение загружается в стек операнда, а затем передается одному из кодов операций JRE, которые его разыскивают. Тем не менее, стек операнда не имеет имени для связывания с нулевым значением. Все, что у него есть, - "null". С помощью некоторого умного кода отслеживания времени выполнения можно получить имя, но это добавит накладные расходы с ограниченным значением.
Из-за этого не существует опции JRE, которая будет включать дополнительную информацию для исключений нулевого указателя.
В этом примере ссылка хранится в локальном слоте 1, который сопоставляется с именем локальной переменной. Но разыменование происходит в invokevirtual инструкции, которая видит только стек "null" в стеке, а затем генерирует исключение:
15 aload_1
16 invokevirtual #5
В равной степени допустимой будет загрузка массива, за которой следует разыменование, но в этом случае нет имени для сопоставления с "нулевым" значением, просто индекс отключен от другого значения.
76 aload 5
78 iconst_0
79 aaload
80 invokevirtual #5
Вы также не можете назначать имена статически каждой команде - этот пример создает много байт-кода, но вы можете видеть, что команда разыменования получит либо objA, либо objB, и вам нужно будет отслеживать это динамически, чтобы сообщить об этом правый, поскольку обе переменные переходят к одной и той же инструкции разыменования:
(myflag ? objA : objB).toString()
Ответ 2
Как только вы JIT-код, это просто встроенная математика указателя, и если какой-либо указатель в собственном коде имеет значение null, он выдает исключение. Это привело бы к разрушительному эффекту производительности, чтобы вернуть эту сборку обратно к исходной переменной и, учитывая, что JIT оптимизирует сгенерированный код на разные уровни, часто даже не представляется возможным.
Ответ 3
Если вы разделите строку на несколько строк, вместо того, чтобы делать несколько вызовов методов в одной строке, или если вы установите точку останова на этой строке и пройдите через линию с помощью отладчика, вы можете выяснить, какая ссылка является нулевой довольно легко.
Ответ 4
Если
Номер строки не является достаточно конкретным потому что линия нарушения может часто содержат многочисленные переменные, которые могли бы вызвали ошибку.
то я предлагаю:
- Разрыв этой строки на несколько строк и присвоение временных значений
NullPointerException
для временных переменных.
- Используйте отладчик и переходите к каждому вызову метода, пока не найдете тот, который вызывает проблему.
Ответ 5
Это, к сожалению, так, как работает Java.
Если это "ваш" код, просто добавьте такие фрагменты, как
if (foo == null) {
throw new NullPointerException("foo == null");
}
сразу после назначения foo. Если foo - это параметр, то сразу же проверяйте его в начале тела метода и вместо этого выведите IllegalArgumentException.
Это поможет вам прояснить ситуацию.
Ответ 6
Вы можете добавить точку останова в исключении нулевого указателя в Eclipse при отладке, чтобы получить точную причину исключения.