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

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

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

+210
20 окт. '08 в 13:18
источник поделиться
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 назначается этому объекту Integer.

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

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

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

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

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

doSomething(null);

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

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

+3621
20 окт. '08 в 13:54
источник

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

+849
20 окт. '08 в 13:20
источник

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

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

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

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

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

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

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

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

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

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

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

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

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

См. также:

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

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

+678
07 июн. '14 в 19:22
источник

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

Как вы должны знать, типы 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[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.

+490
22 июн. '14 в 2:16
источник

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

TypeA objA;

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

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

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
+412
20 окт. '08 в 13:21
источник

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

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

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

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

+350
17 апр. '13 в 2:57
источник

null указатель - это указатель, который указывает на никуда. Когда вы разыменовываете указатель p, вы говорите: "дайте мне данные в месте, сохраненном в" p ". Когда p - null указатель, местоположение, сохраненное в p, nowhere, вы говорите:" дайте мне данные в этом месте " "Нигде" ". Очевидно, что он не может этого сделать, поэтому он null pointer exception.

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

+322
20 окт. '08 в 13:25
источник

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

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

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

Резюме:

  • Используйте модификатор final для обеспечения хорошей инициализации.
  • Избегайте возвращать null в методах, например, возвращая пустые коллекции, если это применимо.
  • Использовать аннотации @NotNull и @Nullable
  • Быстрое сбой и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, если они не должны быть нулевыми.
  • Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  • Предпочитает valueOf() над toString().
  • Используйте методы с нулевым безопасным StringUtils StringUtils.isEmpty(null).
+309
25 июн. '14 в 11:17
источник

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

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

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");
        }
    }
}
+305
24 сент. '13 в 6:01
источник

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

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

  1. декларировать
  2. инициализация

Пример:

  • Объявление: Object object;
  • Инициализация: object = new Object();

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

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

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

+303
28 янв. '12 в 6:45
источник

В Java все переменные, которые вы объявляете, на самом деле являются "ссылками" на объекты (или примитивы), а не сами объекты.

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

Ваша ссылка "указывает" на нуль, таким образом "Null → Pointer".

Объект живет в пространстве памяти VM, и единственный доступ к нему - this использование 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 указывают на null), объект "недоступен". Мы не можем работать с ним, поэтому этот объект готов к сбору мусора, и в какой-то момент виртуальная машина освободит память, используемую этим объектом, и выделит другую.

+298
20 окт. '08 в 20:05
источник

Другое событие 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));
}
+275
25 мая '14 в 6:11
источник

Посмотрите другие вопросы по меткам или Задайте вопрос