2009-07-22 1 views
27

Я всегда задавался вопросом, почему JVM не говорит вам , который указатель является нулевым при вызове NullPointerException.Почему Java не говорит вам, какой указатель имеет значение null?

Номер строки не является достаточно конкретным, поскольку строка нарушения часто может содержать множество переменных, которые могли бы вызвать ошибку.

Есть ли какой-либо флаг компилятора или JVM, который сделает эти сообщения об исключениях более полезными?

+9

Это действительно никогда не было проблемой для меня за 8 лет профессионального профессионала Java. Если у вас есть много ссылок, которые могут быть нулевыми в одной строке, возможно, пора разбить ее на несколько строк. – MattC

+6

@mattc, вы никогда не работаете с кодом библиотеки других народов? –

+0

@MattC: Это часто возникает, когда вам нужно вызвать функцию с несколькими аргументами в условии середины длинного блока if-else-if. Разбить его на несколько строк означало бы поднятие и объявление кучки фиктивных переменных до вершины 'if'. Я бы сказал, что в большинстве случаев это было бы более проблематичным с точки зрения удобочитаемости кода и стиля. – kpozin

ответ

49

Это потому, что разыменование происходит всегда, когда нет имени. Значение загружается в стек операнда, а затем передается одному из кодов операций JRE, которые его разыскивают. Тем не менее, стек операнда не имеет имени для связывания с нулевым значением. Все, что у него есть, - «null». С помощью некоторого умного кода отслеживания времени выполнения можно получить имя, но это добавит накладные расходы с ограниченным значением.

Из-за этого не существует опции JRE, которая включит дополнительную информацию для исключений из нулевого указателя.

В этом примере ссылка хранится в локальном слоте 1, который сопоставляется локальному имени переменной.Но разыменовать происходит в invokevirtual инструкции, которая только видит «нулевое» значение в стеке, а затем бросает исключение:

15 aload_1 
16 invokevirtual #5 

равноценны бы нагрузка массива с последующим разыменованием, но в этом случае нет никакого имени для сопоставления с значением «null», просто индексом другого значения.

76 aload 5 
78 iconst_0 
79 aaload 
80 invokevirtual #5 

Вы не можете выделить имена статически каждую команду либо - это пример дает много байткода, но вы можете видеть, что команда разыменовать получит либо objA или objB, и вам нужно будет отслеживать этот динамически сообщать правильный, поскольку обе переменные потока в одной и той же инструкции разыменовывания:

(myflag ? objA : objB).toString() 
+6

+1 Для фактического объяснения, а не «если вы пишете код« правильный путь », это не произойдет с вами». – Zarkonnen

+2

JVM должна быть в состоянии сказать нам, какая инструкция вызвала NPE, хотя, и я думаю, что это все еще чрезвычайно полезно, например, если выражение «a.b.c.d.e.f» приводит к NPE. –

12

После того, как вы добавили код, это просто встроенная математика указателя, и если какой-либо указатель в собственном коде имеет значение null, он выдает исключение. Было бы разрушительным воздействием на производительность, чтобы вернуть эту сборку обратно к исходной переменной и, учитывая, что JIT оптимизирует сгенерированный код на разных уровнях, часто даже не представляется возможным.

+0

Сбросил мой повтор прямо перед этим сообщением. LOL: o –

+2

@Michael: JIT отслеживает номера строк при генерации собственного кода. Он не отслеживает более высокое разрешение, чем это (подвыражения и т. Д.). –

+0

@Michael: Извините, я говорил от CLI JIT: см. Описание для члена «Default» здесь: http://msdn.microsoft.com/en-us/library/system.diagnostics.debuggableattribute.debuggingmodes.aspx –

0

Если вы нарушаете линию на несколько строк вместо того, чтобы делать несколько вызовов методов в одной строке, или если вы устанавливаете точку останова на этой линии и проходите через линию с помощью отладчика, вы можете выяснить, какая ссылка нулевая довольно легко ,

0

Если

номер строки не хватает конкретных , поскольку виновный линия может часто содержат многочисленные переменные, которые могут вызвавших ошибку.

тогда я предлагаю:

  1. Разбивая эту линию в более чем одной строке и присвоить -generating значения можно NullPointerException временных переменных.
  2. Используйте отладчик и перейдите к каждому вызову метода, пока не найдете тот, который вызывает проблему.
0

Это, к сожалению, так же, как работает Java.

Если это «ваш» код, то просто добавьте фрагменты, как

if (foo == null) { 
    throw new NullPointerException("foo == null"); 
} 

сразу после присвоения Foo. Если foo - это параметр, то сразу проверьте его в начале тела метода и вместо этого выведите IllegalArgumentException.

Это должно помочь вам прояснить ситуацию.

+2

Я думаю, что утверждения 'assert' были бы предпочтительнее над операторами' if'. – kpozin

+0

Установки по умолчанию отключены. Нехорошо. –

+2

Я бы убил всех, кто пришел в обзор кода с таким кодом. – duffymo

0

Вы можете добавить точку останова на исключения нулевого указателя в Eclipse, при отладке, чтобы получить точную причину исключения.

Смежные вопросы