Internal exception java lang nullpointerexception что делать. Что такое исключение NullPointerException и как его исправить

Nokia 10.02.2019

12 ответов

Когда вы объявляете ссылочную переменную (т.е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа 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 присваивается этот объект. Теперь вы можете ссылаться на объект, используя оператор разыменования. (точка).

Exception , о котором вы просили, возникает, когда вы объявляете переменную, но не создаете объект. Если вы попытаетесь разыменовать num . Перед созданием объекта вы получите NullPointerException . В самых тривиальных случаях компилятор поймает проблему и сообщит вам, что "num не может быть инициализировано", но иногда вы пишете код, который непосредственно не создает объект.

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

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

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

DoSomething(null);

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

В качестве альтернативы могут быть случаи, когда цель метода заключается не только в том, чтобы работать с переданным в объекте, и поэтому нулевой параметр может быть приемлемым. В этом случае вам нужно будет проверить параметр null и вести себя по-другому. Вы также должны объяснить это в документации. Например, 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 } }

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

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

Public class Example { public static void main(String args) { Object obj = null; obj.hashCode(); } }

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

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

Хорошим местом для начала является JavaDocs . Они охватывают:

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

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

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

Также, если вы попытаетесь использовать нулевую ссылку с помощью synchronized , это также вызовет это исключение, за JLS :

SynchronizedStatement: synchronized (Expression) Block

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

Итак, у вас есть NullPointerException . Как вы это исправите? Возьмем простой пример, который выдает a 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(); } }

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

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

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() удаляется из метода.

Трассировка, где эти значения взяты из

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

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

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

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

У переменной может быть значение по умолчанию (и setName может помешать ему установить значение null):

Private String name = "";

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

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(); } }

Как вы должны знать, типы Java делятся на примитивные типы (boolean , int и т.д.) и типы ссылок. Типы ссылок в Java позволяют использовать специальное значение null , которое является способом Java, говорящим "нет объекта".

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

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

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

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

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

Вопрос: Как я прочитал стек стека NPE?

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

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

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

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

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

Exception in thread "main" java.lang.NullPointerException

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

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

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

At Test.main(Test.java:4)

Это говорит нам о многом:

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

Если вы подсчитаете строки в файле выше, строка 4 - это та, которую я обозначил комментарием "ЗДЕСЬ".

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

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

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

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

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

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

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

Как это может вызвать NPE?

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

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

Хорошо, если это произошло, трассировка стека будет выглядеть по-другому. В первой строке "at" будет указано, что исключение было выбрано в некоторой строке класса java.lang.String , а строка 4 из Test.java будет второй строкой "at" .

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

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

Public class Test { private static String foo = new String; private static int test(String bar, int pos) { return bar.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.length();

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

Int length = test(foo, 1);

Глядя на первую строчку, как это может вызвать NPE? Существует два способа:

  • Если значение bar равно null , тогда bar будет вызывать NPE.
  • Если значение bar равно null , то вызов length() на нем вызовет NPE.

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

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

Как насчет второго сценария? Итак, мы можем видеть, что pos - 1 , поэтому это означает, что foo должен быть null . Возможно ли это?

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

Private static String foo = new String;

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

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

TypeA objA;

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

См. Также пример ниже:

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

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

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

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

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

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

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

Резюме:

  • Используйте модификатор final для обеспечения хорошей инициализации.
  • Избегайте возвращать null в методах, например, возвращая пустые коллекции, если это применимо.
  • Использовать аннотации @NotNull и @Nullable
  • Быстрое сбой и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, если они не должны быть нулевыми.
  • Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  • Предпочитает valueOf() над toString().
  • Используйте методы с нулевым безопасным StringUtils StringUtils.isEmpty(null) .
  • Java ,
  • Отладка
  • Эта простая статья скорее для начинающих разработчиков Java, хотя я нередко вижу и опытных коллег, которые беспомощно глядят на stack trace, сообщающий о NullPointerException (сокращённо NPE), и не могут сделать никаких выводов без отладчика. Разумеется, до NPE своё приложение лучше не доводить: вам помогут null-аннотации, валидация входных параметров и другие способы. Но когда пациент уже болен, надо его лечить, а не капать на мозги, что он ходил зимой без шапки.

    Итак, вы узнали, что ваше приложение упало с NPE, и у вас есть только stack trace. Возможно, вам прислал его клиент, или вы сами увидели его в логах. Давайте посмотрим, какие выводы из него можно сделать.

    NPE может произойти в трёх случаях:

    1. Его кинули с помощью throw
    2. Кто-то кинул null с помощью throw
    3. Кто-то пытается обратиться по null-ссылке
    Во втором и третьем случае message в объекте исключения всегда null, в первом может быть произвольным. К примеру, java.lang.System.setProperty кидает NPE с сообщением «key can"t be null», если вы передали в качестве key null. Если вы каждый входной параметр своих методов проверяете таким же образом и кидаете исключение с понятным сообщением, то вам остаток этой статьи не потребуется.
    1. Вызов нестатического метода класса
    2. Обращение (чтение или запись) к нестатическому полю
    3. Обращение (чтение или запись) к элементу массива
    4. Чтение length у массива
    5. Неявный вызов метода valueOf при анбоксинге (unboxing)
    Важно понимать, что эти случаи должны произойти именно в той строчке, на которой заканчивается stack trace, а не где-либо ещё.

    Рассмотрим такой код:
    1: class Data { 2: private String val; 3: public Data(String val) {this.val = val;} 4: public String getValue() {return val;} 5: } 6: 7: class Formatter { 8: public static String format(String value) { 9: return value.trim(); 10: } 11: } 12: 13: public class TestNPE { 14: public static String handle(Formatter f, Data d) { 15: return f.format(d.getValue()); 16: } 17: }
    Откуда-то был вызван метод handle с какими-то параметрами, и вы получили:
    Exception in thread "main" java.lang.NullPointerException at TestNPE.handle(TestNPE.java:15)
    В чём причина исключения - в f, d или d.val? Нетрудно заметить, что f в этой строке вообще не читается, так как метод format статический. Конечно, обращаться к статическому методу через экземпляр класса плохо, но такой код встречается (мог, например, появиться после рефакторинга). Так или иначе значение f не может быть причиной исключения. Если бы d был не null, а d.val - null, тогда бы исключение возникло уже внутри метода format (в девятой строчке). Аналогично проблема не могла быть внутри метода getValue, даже если бы он был сложнее. Раз исключение в пятнадцатой строчке, остаётся одна возможная причина: null в параметре d.

    Вот другой пример:
    1: class Formatter { 2: public String format(String value) { 3: return "["+value+"]"; 4: } 5: } 6: 7: public class TestNPE { 8: public static String handle(Formatter f, String s) { 9: if(s.isEmpty()) { 10: return "(none)"; 11: } 12: return f.format(s.trim()); 13: } 14: }
    Снова вызываем метод handle и получаем
    Exception in thread "main" java.lang.NullPointerException at TestNPE.handle(TestNPE.java:12)
    Теперь метод format нестатический, и f вполне может быть источником ошибки. Зато s не может быть ни под каким соусом: в девятой строке уже было обращение к s. Если бы s было null, исключение бы случилось в девятой строке. Просмотр логики кода перед исключением довольно часто помогает отбросить некоторые варианты.

    С логикой, конечно, надо быть внимательным. Предположим, условие в девятой строчке было бы написано так:
    if("".equals(s))
    Теперь в самой строчке обращения к полям и методам s нету, а метод equals корректно обрабатывает null, возвращая false, поэтому в таком случае ошибку в двенадцатой строке мог вызвать как f, так и s. Анализируя вышестоящий код, уточняйте в документации или исходниках, как используемые методы и конструкции реагируют на null. Оператор конкатенации строк +, к примеру, никогда не вызывает NPE.

    Вот такой код (здесь может играть роль версия Java, я использую Oracle JDK 1.7.0.45):
    1: import java.io.PrintWriter; 2: 3: public class TestNPE { 4: public static void dump(PrintWriter pw, MyObject obj) { 5: pw.print(obj); 6: } 7: }
    Вызываем метод dump, получаем такое исключение:
    Exception in thread "main" java.lang.NullPointerException at java.io.PrintWriter.write(PrintWriter.java:473) at java.io.PrintWriter.print(PrintWriter.java:617) at TestNPE.dump(TestNPE.java:5)
    В параметре pw не может быть null, иначе нам не удалось бы войти в метод print. Возможно, null в obj? Легко проверить, что pw.print(null) выводит строку «null» без всяких исключений. Пойдём с конца. Исключение случилось здесь:
    472: public void write(String s) { 473: write(s, 0, s.length()); 474: }
    В строке 473 возможна только одна причина NPE: обращение к методу length строки s. Значит, s содержит null. Как так могло получиться? Поднимемся по стеку выше:
    616: public void print(Object obj) { 617: write(String.valueOf(obj)); 618: }
    В метод write передаётся результат вызова метода String.valueOf. В каком случае он может вернуть null?
    public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
    Единственный возможный вариант - obj не null, но obj.toString() вернул null. Значит, ошибку надо искать в переопределённом методе toString() нашего объекта MyObject. Заметьте, в stack trace MyObject вообще не фигурировал, но проблема именно там. Такой несложный анализ может сэкономить кучу времени на попытки воспроизвести ситуацию в отладчике.

    Не стоит забывать и про коварный автобоксинг. Пусть у нас такой код:
    1: public class TestNPE { 2: public static int getCount(MyContainer obj) { 3: return obj.getCount(); 4: } 5: }
    И такое исключение:
    Exception in thread "main" java.lang.NullPointerException at TestNPE.getCount(TestNPE.java:3)
    На первый взгляд единственный вариант - это null в параметре obj. Но следует взглянуть на класс MyContainer:
    import java.util.List; public class MyContainer { List elements; public MyContainer(List elements) { this.elements = elements; } public Integer getCount() { return elements == null ? null: elements.size(); } }
    Мы видим, что getCount() возвращает Integer, который автоматически превращается в int именно в третьей строке TestNPE.java, а значит, если getCount() вернул null, произойдёт именно такое исключение, которое мы видим. Обнаружив класс, подобный классу MyContainer, посмотрите в истории системы контроля версий, кто его автор, и насыпьте ему крошек под одеяло.

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

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

    6 Answers


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

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

    Public class Example { public static void main(String args) { Object obj = null; obj.hashCode(); } }

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

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

    телефоне избежать ошибка

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

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

    2018-11-27T00:00Z

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

    Как вы должны знать, типы Java делятся на примитивные типы (boolean , int и т. Д.) И ссылочные типы . Типы ссылок в Java позволяют использовать специальное значение null которое является Java-способом выражения «no object».

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

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

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

    Существует много способов использования null значения, которое приведет к NullPointerException . Фактически, единственное, что вы можете сделать с null не вызывая NPE, это:

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

    Вопрос: Как я прочитал стек стека NPE?

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

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

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

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

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

    Exception in thread "main" java.lang.NullPointerException

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

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

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

    At Test.main(Test.java:4)

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

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

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

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

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

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

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

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

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

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

    Как это может вызвать NPE?

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

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

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

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

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

    Public class Test { private static String foo = new String; private static int test(String bar, int pos) { return bar.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) $

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

    Return args.length();

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

    Int length = test(foo, 1);

    Глядя на первую строчку, как это может вызвать NPE? Существует два способа:

    • Если значение bar равно null тогда bar будет вызывать NPE.
    • Если значение bar равно null то вызов length() на нем вызовет NPE.

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

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

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

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

    Private static String foo = new String;

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

    Java1

    2018-12-11T00:00Z

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

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

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

    Java2

    2018-12-18T00:00Z

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

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

    Резюме:

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

    Java3

    2018-12-25T00:00Z

    В Java все находится в форме класса.

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

    1. декларировать
    2. инициализация
    • Декларация: Object a;
    • Инициализация: a=new Object();

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

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

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

    Java4

    2019-01-01T00:00Z

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

    String phrases = new String; 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)); }

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

    Int x; x = 10;

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

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

    Integer num; num = new Integer(10);

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

    Во второй строке, ключевое слово new используется для создания объекта типа Integer . Этот объект имеет адрес в памяти, который присваивается переменной num . Теперь, с помощью переменной num вы можете обратиться к объекту используя оператора разыменования. .

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

    num may not have been initialized

    Что говорит: «возможно, переменная num не инициализирована».

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

    Public void doSomething(Integer num){ // Работаем с num }

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

    DoSomething(null);

    В этом случае значение переменной num будет null . Лучшим способом избежать данного исключения будет проверка на равенство нулю. Как результат, функция doSomething должна быть переписана следующим образом:

    Public void doSomething(Integer num){ if (num != null) { // Работаем с num } }

    Как альтернативный вариант предыдущему примеру вы можете сообщить вызывающему коду, что метод был вызван с неверными параметрами, например, с помощью IllegalArgumentException .

    Public void doSomething(Integer num){ if (num == null) throw new IllegalArgumentException("Num не должен быть null"); // Работаем с num }



    Рекомендуем почитать

    Наверх