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

StackOverflow https://stackoverflow.com/questions/218384

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

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

int x;
x = 10;

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

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

Integer num;
num = new Integer(10);

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

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

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

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

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

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

В этом случае вы не создаете объект obj , а скорее предполагаете, что он был создан до вызова метода 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
    }
}

Наконец, Как определить исключение & amp; вызывать использование Stack Trace

Другие советы

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

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

public class Example {

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

}

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

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

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

Хорошим местом для начала является JavaDocs - файлы.Они это предусмотрели:

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

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

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

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

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение Выражения равно null, 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 способ).Посмотрите на строку и проверьте, какие значения равны null, добавив операторы ведения журнала или используя отладчик.Мы выясняем, что s равно нулю, и вызывающий length метод на нем выдает исключение.Мы можем видеть, что программа перестает выдавать исключение, когда s.length() удаляется из метода.

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

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

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

Где находится 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();
    }
}

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

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

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

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

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

оператор с пометкой "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". НЕПРАВИЛЬНО!! Это не чушь собачья.Это трассировка стека ...и это обеспечивает важная информация это поможет вам отследить ошибку в вашем коде, если вы потратите время на ее внимательное прочтение.

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

Exception in thread "main" java.lang.NullPointerException

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

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

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

at Test.main(Test.java:4)

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

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

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

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

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

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

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

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

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

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

Как это может привести к отказу от NPE?

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

Но (я слышу, как вы говорите) что, если 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.

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

TypeA objA;

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

См. также приведенный ниже пример:

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

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

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

    Ссылка: http://docs.oracle. ком / JavaSE / 8 / документы / API / Java / языки / NullPointerException.html

    Указатель null - это указатель в никуда. Когда вы разыменовываете указатель p , вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p является указателем null , местоположение, сохраненное в p , равно нигде , вы говорите " мне данные в месте "никуда". Очевидно, что он не может этого сделать, поэтому выдает исключение нулевого указателя .

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

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

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

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

    Краткие сведения:

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

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

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

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

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

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

    1. Объявлять
    2. Инициализация

    Пример:

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

    То же самое и с концепцией массива:

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

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

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

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

    Ваша ссылка " указывающая " к нулю, таким образом, "Null - > Указатель & Quot;.

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

    Другое возникновение 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));
    }
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top