Что такое исключение NullPointerException и как его исправить?


210

Что такое исключения нулевого указателя ( java.lang.NullPointerException) и что их вызывает?

Какие методы / инструменты можно использовать, чтобы определить причину, чтобы исключить преждевременное завершение программы из-за исключения?

Ответы:


3767

Когда вы объявляете ссылочную переменную (т.е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа int:

int x;
x = 10;

В этом примере переменная xявляется intи Java будет инициализировать его 0для вас. Когда вы присваиваете ему значение 10во второй строке, ваше значение 10записывается в ячейку памяти, на которую ссылается x.

Но когда вы пытаетесь объявить ссылочный тип , происходит что-то другое. Возьмите следующий код:

Integer num;
num = new Integer(10);

Первая строка объявляет переменную с именем num, но на самом деле она еще не содержит примитивного значения. Вместо этого он содержит указатель (потому что тип Integerявляется ссылочным типом). Поскольку вы еще не сказали, на что указывать, Java устанавливает его null, что означает « я ни на что не указываю ».

Во второй строке newключевое слово используется для создания экземпляра (или создания) объекта типа, Integerи numэтому Integerобъекту назначается переменная-указатель .

NullPointerExceptionПроисходит , когда вы объявляете переменную , но не создать объект и присвоить переменной , прежде чем пытаться использовать содержимое переменной ( так называемый разыменования ). Таким образом, вы указываете на то, чего на самом деле не существует.

Разыменование обычно происходит при использовании . для доступа к методу или полю или при [индексировании массива.

Если вы попытаетесь разыменовать numДО создания объекта, вы получитеNullPointerException . В самых тривиальных случаях компилятор поймает проблему и сообщит вам, что " num may not have been initialized,", но иногда вы можете написать код, который не создает объект напрямую.

Например, у вас может быть следующий метод:

public void doSomething(SomeObject obj) {
   //do something to obj
}

В этом случае вы не создаете объект obj, а скорее предполагаете, что он был создан до вызова doSomething()метода. Обратите внимание, что метод можно вызвать так:

doSomething(null);

В каком случае objэто null. Если метод предназначен для того, чтобы что-то сделать с переданным объектом, целесообразно выбросить его, NullPointerExceptionпотому что это ошибка программиста, и программисту потребуется эта информация для целей отладки. Пожалуйста, включите имя переменной объекта в сообщение об исключении, например

Objects.requireNonNull(a, "a");

Альтернативно, могут быть случаи, когда целью метода является не только работа с переданным объектом, и, следовательно, нулевой параметр может быть приемлемым. В этом случае вам нужно будет проверить нулевой параметр и вести себя по-другому. Вы также должны объяснить это в документации. Например, doSomething()может быть написано как:

/**
  * @param obj An optional foo for ____. May be null, in which case 
  *  the result will be ____.
  */
public void doSomething(SomeObject obj) {
    if(obj == null) {
       //do something
    } else {
       //do something else
    }
}

Наконец, как определить исключение и причину, используя Stack Trace

Какие методы / инструменты можно использовать, чтобы определить причину, чтобы исключить преждевременное завершение программы из-за исключения?

Сонар с findbugs может обнаружить NPE. Может ли сонар ловить исключения нулевого указателя, вызванные JVM динамически


558
«Лучший способ избежать такого типа исключений - всегда проверять нулевое значение, если вы сами не создавали объект». Если вызывающая сторона передает значение null, но значение null не является допустимым аргументом для метода, тогда будет правильным бросить исключение обратно в вызывающую функцию, потому что это ошибка вызывающей стороны. Незаметное игнорирование неверного ввода и ничего не делать в методе - очень плохой совет, потому что он скрывает проблему.
Boann

104
Я хотел бы добавить замечание об этом посте, объясняющее, что даже назначения примитивам могут вызывать NPE при использовании автобокса: int a=bмогут бросить NPE, если b является Integer. Есть случаи, когда это сбивает с толку для отладки.
Саймон Фишер

58
Можно ли захватить NPE, брошенный веб-приложением, из веб-браузера? Как это будет показано в исходной странице просмотра из веб-браузера ...
Сид

76
Да, проверьте, равен ли объект нулю, прежде чем вызывать для него метод или пытаться получить доступ к переменной, которую он может иметь. Иногда структурирование вашего кода может помочь избежать исключения нулевого указателя. Например, при проверке входной строки с константной строкой вы должны начинать с константной строки, как здесь: if ("SomeString" .equals (inputString)) {} // даже если inputString равен нулю, исключение не выдается. Таким образом, есть несколько вещей, которые вы можете сделать, чтобы быть в безопасности.
Роза

78
Еще один способ избежать NullPointerExceptionпроблем в вашем коде является использование @Nullableи @NotNullаннотации. Следующий ответ имеет больше информации об этом. Хотя этот ответ относится конкретно к IntelliJ IDE, он также применим к другим инструментам, как это описано в комментариях. (Кстати, мне не разрешено редактировать этот ответ напрямую, возможно, автор может добавить его?)
Arjan Mels

880

NullPointerExceptionЭто исключения, которые возникают, когда вы пытаетесь использовать ссылку, которая указывает на отсутствие места в памяти (ноль), как если бы оно ссылалось на объект. Вызов метода с нулевой ссылкой или попытка доступа к полю с нулевой ссылкой вызовет a NullPointerException. Это наиболее распространенные, но другие способы перечислены наNullPointerException странице javadoc.

Вероятно, самый быстрый пример кода, который я мог бы придумать для иллюстрации, был NullPointerExceptionбы:

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

На первой линии внутри main я явно устанавливаю Objectссылку objна null. Это означает, что у меня есть ссылка, но она не указывает ни на какой объект. После этого я пытаюсь обработать ссылку так, как будто она указывает на объект, вызывая метод для него. Это приводит кNullPointerException что в местоположении, на которое указывает ссылка, нет кода для выполнения.

(Это техническая проблема, но я думаю, что стоит упомянуть: ссылка, указывающая на ноль, не совпадает с указателем C, указывающим на недопустимое расположение в памяти. Нулевой указатель буквально никуда не указывает , что несколько отличается от указывая на местоположение, которое оказывается недействительным.)


49
Я понял все, что вы там написали, но только потому, что я некоторое время кодировал и знаю, что такое «указатель» и «ссылка» (и что такое «ноль»). Когда я пытаюсь погрузиться прямо в подобные объяснения, мои ученики смотрят на меня с косоглазием, потому что не хватает предыстории.
MMR

33
@mmr: Спасибо за отзыв, вы делаете правильное замечание. В интернете сложно реально судить, где кто-то находится, и на каком уровне безопасно начинать объяснение. Я попробую пересмотреть это снова.
Билл Ящерица

22
Более распространенный способ получить исключение NullPointerException на практике - забыть явно инициализировать переменную-член чем-то другим, чем nullперед ее использованием, например, так . С локальными переменными компилятор поймает эту ошибку, но в этом случае это не так. Может быть, это будет полезным дополнением к вашему ответу?
Илмари Каронен

6
@EJP Я думаю, что ваши очки верны, поэтому я обновил ответ, чтобы он был более понятным и чтобы я не говорил «указывает на ноль» там, где это было.
Стив Пауэлл

5
@ StevePowell Я давным-давно указал, что не хочу, чтобы мой ответ изменился. Пожалуйста, уважайте намерения первоначального автора.
Билл Ящерица

697

Что такое исключение NullPointerException?

Хорошее место для начала - JavaDocs . У них есть это покрыто:

Брошенный, когда приложение пытается использовать нуль в случае, когда объект требуется. Это включает:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Принимая длину нуля, как если бы это был массив.
  • Доступ или изменение пустых слотов, как если бы это был массив.
  • Бросить ноль, как если бы это было значение Throwable.

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

Это также тот случай, когда вы попытаетесь использовать нулевую ссылку с synchronized, это также сгенерирует это исключение для JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение Expression равно нулю, генерируется a NullPointerException.

Как мне это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Давайте рассмотрим простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Определить нулевые значения

Первый шаг - точно определить, какие значения вызывают исключение . Для этого нам нужно сделать некоторую отладку. Важно научиться читать трассировку стека . Это покажет вам, где было выброшено исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выдается в строке 13 (в printStringметоде). Посмотрите на строку и проверьте, какие значения являются нулевыми, добавив операторы регистрации или используя отладчик . Мы выясняем, что sэто null, и вызов lengthметода для него вызывает исключение. Мы видим, что программа перестает генерировать исключение, когдаs.length() удаляется из метода.

Проследите, откуда берутся эти значения

Затем проверьте, откуда это значение. Следуя указаниям метода, мы видим, что sпередается printString(name)в print()метод, иthis.name имеет значение null.

Трассировка, где эти значения должны быть установлены

Где this.nameустановлен? В setName(String)методе. После некоторой дополнительной отладки мы видим, что этот метод вообще не вызывается. Если метод был вызван, обязательно проверьте порядок вызова этих методов, и метод set не вызывается после метода print.

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName()перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчаниюsetNameможет помешать ей иметь значение null):

private String name = "";

Любой метод printили printStringможет проверить на ноль , например:

printString((name == null) ? "" : name);

Или вы можете создать класс так, чтобы он name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

Смотрите также:

Я до сих пор не могу найти проблему

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


44
+1 Хорошо иметь пример, который включает в себя прохождение трассировки стека; важно показать, почему чтение важно для отладки NPE. (и почему мы почти всегда ищем трассировку стека, когда кто-то отправляет вопрос об ошибке)
Деннис Мэн,

16
Вы упомянули отладку ... Как это работает? Я уже давно изучаю эту тему, но ничего не могу найти. Я уверен, что такой замечательный учитель, как ты, может научить меня этому за секунду! Спасибо! :-)
Ruchir Baronia

15
@RuchirBaronia Отладчик позволяет пошагово проходить через программу, чтобы увидеть, какие методы вызываются и как меняются переменные. В IDE должны быть инструменты для этого. См. Например, vogella.com/tutorials/EclipseDebugging/article.html .
ФГБ

15
@RuchirBaronia Вы устанавливаете точки останова на методах вокруг любых исключений NullPointerException, как показано в трассировке стека, и сравниваете значения переменных с тем, что вы ожидаете от них. Если вы знаете, что переменная равна нулю, а это не должно быть, тогда вы можете установить точки останова вокруг любого кода, который изменяет значение. Вы также можете использовать условные контрольные точки, которые сообщат вам об изменении значения.
ФГБ

6
Установка объектов String в пустую строку в качестве значения по умолчанию считается плохой практикой.
Крошечный

502

Вопрос: что вызывает NullPointerException (NPE)?

Как вы знаете, типы Java разделены на примитивных типов ( boolean, intи т.д.) и ссылочные типы . Ссылочные типы в Java позволяют использовать специальное значениеnull которое в Java означает «нет объекта».

A NullPointerExceptionгенерируется во время выполнения всякий раз, когда ваша программа пытается использовать a, nullкак если бы это была реальная ссылка. Например, если вы напишите это:

public class Test {
    public static void main(String[] args) {
        String foo = null;
        int length = foo.length();   // HERE
    }
}

оператор, помеченный «ЗДЕСЬ», попытается запустить length()метод для nullссылки, и это приведет кNullPointerException .

Есть много способов, которыми вы можете использовать nullзначение, которое приведет к NullPointerException. Фактически, единственные вещи, которые вы можете сделать с помощью nullNPE:

  • присвоить его ссылочной переменной или прочитать из ссылочной переменной,
  • назначить его элементу массива или прочитать его из элемента массива (при условии, что ссылка на массив сама по себе не равна нулю!),
  • передать его в качестве параметра или вернуть в результате, или
  • проверить его с помощью ==или !=операторов, или instanceof.

Вопрос: Как мне прочитать трассировку стека NPE?

Предположим, что я скомпилировал и запустил программу выше:

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.main(Test.java:4)
$

Первое наблюдение: компиляция удалась! Проблема в программе НЕ является ошибкой компиляции. Это ошибка во время выполнения . (Некоторые IDE могут предупредить, что ваша программа всегда выдает исключение ... но стандартjavac компилятор этого не делает.)

Второе наблюдение: когда я запускаю программу, она выводит две строки «gobbledy-gook». НЕПРАВИЛЬНО!! Это не болтунья. Это трассировка стека ... и она предоставляет важную информацию , которая поможет вам отследить ошибку в вашем коде, если вы потратите время на ее тщательное чтение.

Итак, давайте посмотрим, что он говорит:

Exception in thread "main" java.lang.NullPointerException

Первая строка трассировки стека говорит вам несколько вещей:

  • Он сообщает вам имя потока Java, в который было сгенерировано исключение. Для простой программы с одним потоком (как этот) это будет "основной". Давайте двигаться дальше ...
  • Он сообщает вам полное имя исключения, которое было сгенерировано; то есть java.lang.NullPointerException.
  • Если с исключением связано сообщение об ошибке, оно будет выведено после имени исключения. NullPointerExceptionнеобычно в этом отношении, потому что это редко имеет сообщение об ошибке.

Вторая строка является наиболее важной в диагностике NPE.

at Test.main(Test.java:4)

Это говорит нам о нескольких вещах:

  • «at Test.main» говорит, что мы были в mainметоде Testкласса.
  • «Test.java:4» дает исходное имя файла класса, И он говорит нам, что оператор, где это произошло, находится в строке 4 файла.

Если вы посчитаете строки в файле выше, то строка 4 будет помечена комментарием «ЗДЕСЬ».

Обратите внимание, что в более сложном примере в трассировке стека NPE будет много строк. Но вы можете быть уверены, что вторая строка (первая строка «at») скажет вам, куда был брошен NPE 1 .

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

1 - не совсем верно. Есть вещи, называемые вложенными исключениями ...

Вопрос: Как я могу отследить причину исключения NPE в моем коде?

Это сложная часть. Краткий ответ - применить логический вывод к свидетельству, предоставленному трассировкой стека, исходному коду и соответствующей документации API.

Давайте сначала проиллюстрируем на простом примере (выше). Мы начнем с того, что посмотрим на строку, которую трассировка стека сказала нам, где произошло NPE:

int length = foo.length(); // HERE

Как это может бросить NPE?

На самом деле, есть только один путь: это может произойти, только если fooимеет значение null. Затем мы пытаемся запустить length()метод nullи ... Взрыв!

Но (я слышу, вы говорите), что если бы NPE был брошен внутри length() вызова метода?

Ну, если бы это произошло, трассировка стека выглядела бы иначе. Первая строка «at» будет означать, что исключение было сгенерировано в некоторой строке в java.lang.Stringклассе и в строке 4Test.java будет второй строкой «at».

Так откуда это nullвзялось? В этом случае это очевидно, и очевидно, что нам нужно сделать, чтобы это исправить. (Присвойте ненулевое значениеfoo .)

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

public class Test {

    private static String[] foo = new String[2];

    private static int test(String[] bar, int pos) {
        return bar[pos].length();
    }

    public static void main(String[] args) {
        int length = test(foo, 1);
    }
}

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.test(Test.java:6)
    at Test.main(Test.java:10)
$ 

Итак, теперь у нас есть две строки "at". Первый для этой строки:

return args[pos].length();

и второй для этой строки:

int length = test(foo, 1);

Глядя на первую строку, как это может бросить NPE? Есть два способа:

  • Если значение bar- nullто bar[pos]бросит NPE.
  • Если значение bar[pos]равно nullтогда, вызов length()его бросит NPE.

Далее нам нужно выяснить, какой из этих сценариев объясняет, что на самом деле происходит. Мы начнем с изучения первого:

Откуда barберутся? Это параметр для testвызова метода, и если мы посмотрим, как он testбыл вызван, мы увидим, что он исходит из fooстатической переменной. Кроме того, мы можем ясно видеть, что мы инициализировали fooненулевое значение. Этого достаточно, чтобы предварительно отклонить это объяснение. (Теоретически, что-то еще может измениться foo наnull ... но здесь этого не происходит.)

Так что насчет нашего второго сценария? Ну, мы можем видеть, что posэто 1так, значит, это foo[1]должно быть null. Это возможно?

В самом деле! И это проблема. Когда мы инициализируем так:

private static String[] foo = new String[2];

мы выделяем String[]с двумя элементами , которые инициализированы дляnull . После этого мы не изменили содержимое foo... так foo[1]будет и дальше null.


426

Это как будто вы пытаетесь получить доступ к объекту, который есть null. Рассмотрим пример ниже:

TypeA objA;

В настоящее время вы только что объявили этот объект, но не инициализировали или не создали его экземпляр . И всякий раз, когда вы пытаетесь получить доступ к какому-либо свойству или методу, оно выдает, NullPointerExceptionчто имеет смысл

Смотрите также этот пример ниже:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown

1
Если мы дадим System.out.println (a.length ()); // Будет сгенерировано исключение NullPointerException, чтобы пропустить это, мы можем обработать блок try catch. спасибо
Виджая Варма Ланке

360

Исключение пустого указателя выдается, когда приложение пытается использовать нулевое значение в случае, когда требуется объект. Это включает:

  1. Вызов метода экземпляра nullобъекта.
  2. Доступ или изменение поля nullобъекта.
  3. Принимая длину, nullкак если бы это был массив.
  4. Доступ или изменение слотов, nullкак если бы это был массив.
  5. Бросать, nullкак если бы это было значение Throwable.

Приложения должны генерировать экземпляры этого класса, чтобы указать на другое незаконное использование nullобъекта.

Ссылка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html


12
Будьте проще, мне нравится этот ответ, добавьте его, если считаете правильным - Доступ к неинициализированному атрибуту объекта
Эмилиано

5
@Emiliano - простой доступ к инициализированному атрибуту не вызывает NPE. Именно то, что вы >> делаете << со значением неинициализированного атрибута, вызывает NPE.
Стивен С.

1
Если вы хотите больше случаев: 1) использование nullцели как цели synchronized, 2) использование nullцели как цели switch, и распаковка null.
Стивен К

334

nullУказатель один , который указывает в никуда. Когда вы разыменования указателя p, вы говорите , «дайте мне данные на месте , хранящегося в„р“. Когда pэто nullуказатель, расположение хранится в pэто nowhere, вы говорите , „дайте мне данные на месте "нигде“. Очевидно, он не может этого сделать, поэтому он выбрасывает null pointer exception.

В общем, это потому, что что-то не было правильно инициализировано.


2
Мы создаем базу данных? -> NULLнаписано как nullв Java. И это чувствительная к регистру вещь.
bvdb

3
«Указатель NULL - это указатель, который указывает на никуда», я не согласен. Нулевые указатели не указывают ни на что, они указывают на нулевые значения.
TheRealChx101

2
@ TheRealChx101 Нулевой указатель и указатель на нулевое значение - это разные вещи - нулевой указатель не указывает на нулевое значение. Предположим, у вас есть указатель на указатель: указатель A указывает на указатель B, а указатель B равен нулю. В этом случае указатель A указывает на нулевое значение, а указатель B является нулевым указателем.
MrZebra

322

Уже есть много объяснений, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать передовым методам, чтобы NullPointerExceptionвообще избежать их .

Смотрите также: хороший список лучших практик

Я бы добавил, что очень важно, хорошо использовать finalмодификатор. Использование модификатора final, когда это применимо в Java

Резюме:

  1. Использовать final модификатор для обеспечения хорошей инициализации.
  2. Избегайте возврата пустых значений в методы, например возвращайте пустые коллекции, когда это применимо.
  3. Используйте аннотации @NotNullи@Nullable
  4. Быстро завершитесь неудачей и используйте утверждения, чтобы избежать распространения нулевых объектов по всему приложению, когда они не должны быть нулевыми.
  5. Сначала используйте equals с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитаю valueOf()более toString().
  7. Используйте нулевые безопасные StringUtilsметоды StringUtils.isEmpty(null).
  8. Используйте Java 8 Необязательный в качестве возвращаемого значения в методах. Необязательный класс предоставляет решение для представления необязательных значений вместо нулевых ссылок.

4
В проектах j2ee исключение Nullpointer является очень распространенным. В некоторых случаях ссылочные переменные получают нулевые значения. Так что Вы должны правильно проверять инициализацию переменной. И во время условного оператора вы всегда должны проверять, что флаг или ссылка содержат ноль или не похожи: - if (flag! = 0) {код, который использует флаг}
Амареш Паттанаяк

14
Стоит отметить, что некоторые IDE (например, Eclipse) предлагают автоматический анализ недействительности на основе настраиваемых аннотаций (например, @Nullableкак указано выше) и предупреждают о возможных ошибках. Также возможно вывести и сгенерировать такие аннотации (например, IntelliJ может сделать это) на основе существующей структуры кода.
Ян Чимиак

4
Первое, что нужно сделать, прежде чем использовать обнуляемый объект, вы должны проверить, является ли он пустым, используя. if (obj==null)Если он нулевой, то вы должны написать код для обработки этого.
Lakmal Vithanage

4
IMO, желательно избегать возврата пустых объектов в методы, когда это возможно, и использовать аннотацию, когда нулевые входные параметры не разрешены, чтобы, по контракту, уменьшить количество if, если в коде и улучшить удобочитаемость кода.
LG

4
Прочитайте это ... прежде чем принять эти "лучшие практики" как истину: assignice.com/blog/archives/27
Стивен С.

317

В Java все (кроме примитивных типов) имеет форму класса.

Если вы хотите использовать какой-либо объект, у вас есть две фазы:

  1. Declare
  2. инициализация

Пример:

  • Декларация: Object object;
  • Инициализация: object = new Object();

То же самое для концепции массива:

  • Декларация: Item item[] = new Item[5];
  • Инициализация: item[0] = new Item();

Если вы не даете раздел инициализации, то NullPointerExceptionвозникают.


3
NullPointerException часто возникает при вызове метода экземпляра. Например, если вы объявляете ссылку, но не указываете на какой-либо экземпляр, NullPointerException будет происходить при вызове его метода. такие как: YourClass ref = null; // или ref = anotherRef; // но anotherRef не указал ни одного экземпляра ref.someMethod (); // он выдаст исключение NullPointerException. Как правило, исправьте это следующим образом: перед вызовом метода определите, является ли ссылка нулевой. например: if (yourRef! = null) {yourRef.someMethod (); }
Sunhang

2
Или используйте захват исключений: например: try {yourRef.someMethod (); } catch (NullPointerException e) {// TODO}
солнечный день


316

Исключение нулевого указателя - это индикатор того, что вы используете объект без его инициализации.

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

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

Код ниже дает исключение нулевого указателя.

public class School {

    Student student;

    public School() {
        try {
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

Потому что вы используете student, но вы забыли инициализировать его, как в правильном коде, показанном ниже:

public class School {

    Student student;

    public School() {
        try {
            student = new Student();
            student.setId(12);
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

7
Хотя это хороший пример, могу ли я спросить, что это добавляет к вопросу, который еще не охвачен всеми другими ответами?
Мистик

13
Просто неуместно использовать слово «неинициализированный» здесь. Пример, который вы показали, на самом деле «инициализирован» и инициализирован нулем. Для неинициализированных переменных компилятор будет вам жаловаться.
Адриан Шум

2
NPE может быть индикатором того, что вы используете неинициализированное поле. Это может быть индикатором того, что вы занимаетесь другими делами. Упрощение единственной причины, как это, не помогает кому-то решить проблемы NPE ... если действительная причина не в этом.
Стивен С,

309

На яве все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не на сами объекты.

Когда вы пытаетесь выполнить один метод объекта, ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, ноль, void, nada), то метод не выполняется. Затем среда выполнения сообщит вам об этом, выдав исключение NullPointerException.

Ваша ссылка «указывает» на ноль, то есть «Нуль -> Указатель».

Объект живет в пространстве памяти ВМ, и единственный способ получить к нему доступ - использовать thisссылки. Возьмите этот пример:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

И в другом месте в вашем коде:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

Это важно знать: когда больше нет ссылок на объект (в приведенном выше примере, когда referenceи otherReferenceоба указывают на ноль), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сборке мусора, и в какой-то момент виртуальная машина освободит память, используемую этим объектом, и выделит другой.


281

Другое возникновение a NullPointerExceptionпроисходит, когда кто-то объявляет массив объектов, а затем сразу же пытается разыменовать элементы внутри него.

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

Этого конкретного NPE можно избежать, если изменить порядок сравнения; а именно, использовать .equalsна гарантированном ненулевом объекте.

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

Вы должны инициализировать элементы в массиве, прежде чем обращаться к ним или разыменовывать их.

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

операция над неинициализированным объектом на уровне экземпляра (не на уровне класса) приведет к исключению NullPointerException. операция должна быть специфической для конкретного экземпляра. если операция находится на уровне класса, например, при вызове статического метода для неинициализированного объекта, она не вызовет исключение NullPointerException. Даже примитивные объекты класса-оболочки генерируют исключение NullPointerException.
Шайлендра Сингх

1. NullPointerException - это исключение RuntimeException, то есть оно появится, когда ваша программа запущена, а не во время компиляции. :(, но большая часть IDE поможет вам обнаружить это. 2. Сведите к минимуму использование ключевого слова «null» в операторах присваивания. :) Ссылочный URL:
tomj0101

@ tomj0101 Мне совершенно непонятно, почему вы сделали этот комментарий ... Но, к вашему второму моменту, шаблон до этого Optionalдолжен был возвращать ноль. Ключевое слово в порядке. Знание того, как защититься, очень важно. Это предлагает один общий случай этого и способы смягчить это.
Макото

NullPointerException - это исключение во время выполнения, которое не рекомендуется перехватывать, а вместо этого избегать.
Шому

2
@Shomu: В какой момент я даже предлагаю его поймать?
Макото
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.