Що таке NullPointerException, і як це виправити?


210

Що таке виключення Null Pointer ( java.lang.NullPointerException) і що їх викликає?

Які методи / інструменти можна використовувати для визначення причини, щоб ви зупинили виняток від того, щоб програма передчасно припинилась?

Відповіді:


3764

Коли ви оголошуєте опорну змінну (тобто об'єкт), ви дійсно створюєте вказівник на об’єкт. Розглянемо наступний код, де ви оголошуєте змінну примітивного типу 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 Dynamically


558
"Найкращий спосіб уникнути цього виду виключень - це завжди перевіряти наявність нуля, коли ви самі не створили об'єкт." Якщо абонент передає null, але null не є коректним аргументом для методу, тоді правильно повернути виняток у виклику, оскільки це помилка абонента. Мовчазне ігнорування недійсного введення даних і нічого не роблячи в методі є вкрай поганою порадою, оскільки це приховує проблему.
Боан

104
Я додам зауваження до цього допису, пояснюючи, що навіть призначення примітивів може спричинити int a=bNPE при використанні автобоксингу: може кинути NPE, якщо b - an Integer. Є випадки, коли це заплутано для налагодження.
Саймон Фішер

58
Чи можливо зафіксувати NPE, кинуту веб-переглядачем, з веб-браузера? Як це відобразиться у джерелі сторінки перегляду з веб-браузера ..
Sid

76
Так, перевірте, чи об'єкт дорівнює нулю, перш ніж викликати на ньому метод або спробувати отримати доступ до змінної, яка може бути. Інколи структурування коду може допомогти уникнути нульового виключення покажчика. наприклад, перевіряючи рядок введення постійною рядком, ви повинні починати з постійної рядка, як тут: if ("SomeString" .equals (inputString)) {} // навіть якщо inputString є недійсним, виняток не кидається. Тож є купа речей, які ви можете зробити, щоб спробувати бути в безпеці.
Роза

78
Додатковим способом уникнення NullPointerExceptionпроблем у вашому коді є використання @Nullableта @NotNullпримітки. Наступна відповідь має більше інформації щодо цього. Хоча ця відповідь специфічно стосується IntelliJ IDE, вона також застосовна до інших інструментів, як це стосується коментарів з цих коментарів. (До речі, мені не дозволяють редагувати цю відповідь безпосередньо, можливо, автор може її додати?)
Arjan Mels

879

NullPointerExceptions - це винятки, які виникають при спробі використовувати посилання, яке вказує на відсутність місця в пам'яті (null), як ніби воно посилається на об'єкт. Виклик методу з нульовою посиланням або спробу доступу до поля нульової посилання викликає a NullPointerException. Це найпоширеніші, але інші способи перераховані на сторінці NullPointerExceptionjavadoc.

Можливо, найшвидший приклад коду, який я міг би придумати для ілюстрації, був NullPointerExceptionби:

public class Example {

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

}

У першому рядку всередині mainя чітко встановлюю Objectпосилання, objрівне null. Це означає, що у мене є посилання, але воно не вказує на жоден об'єкт. Після цього я намагаюся трактувати посилання так, ніби він вказує на об'єкт, викликаючи метод на ньому. Це призводить до того, NullPointerExceptionщо в місці, на яке вказує посилання, немає коду для виконання.

(Це технічність, але я думаю, що тут згадується: Посилання, що вказує на null, не те саме, що вказівник C, який вказує на неправильне місце пам'яті. Нульовий покажчик буквально нікуди не вказує , що тонко відрізняється, ніж вказує на місце, яке, можливо, недійсне.)


49
Я зрозумів усе, що ви там написали, але тільки тому, що я кодував деякий час і знаю, що таке "вказівник" і "посилання" (і що з цього приводу є нульовим). Коли я намагаюсь зануритися прямо в такі пояснення, мої студенти дивляться на мене переплетеними, тому що недостатньо досвіду.
mmr

33
@mmr: Дякую за відгук, ви робите дійсну точку. В Інтернеті важко реально судити, де хто знаходиться, і на якому рівні безпечно почати пояснення. Я спробую переглянути це ще раз.
Білл Ящірка

22
Більш поширеним способом отримати NullPointerException на практиці буде забути явно ініціалізувати змінну члена до чогось іншого, ніж nullперед її використанням, як це . За допомогою локальних змінних компілятор вловлює цю помилку, але в цьому випадку це не так. Може, це стане корисним доповненням до вашої відповіді?
Ільмарі Каронен

6
@EJP Я вважаю, що ваші бали справедливі, тому я оновив відповідь, щоб бути зрозумілішою і уникати того, щоб сказати "пункти до нуля", де це було.
Стів Пауелл

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

696

Що таке NullPointerException?

Гарне місце для початку - JavaDocs . Вони охоплюють це:

Викидається, коли програма намагається використовувати null у випадку, коли потрібен об’єкт. До них належать:

  • Виклик методу екземпляра нульового об'єкта.
  • Доступ або зміна поля нульового об'єкта.
  • Приймаючи довжину нуля, ніби це масив.
  • Доступ до або змінення слотів null так, ніби це масив.
  • Викидання нуля, як ніби це значення, що викидається.

Програми повинні кидати екземпляри цього класу, щоб вказати на інші незаконні використання нульового об’єкта.

Так само буває, що якщо ви спробуєте використовувати нульову посилання на synchronized, це також викине цей виняток, відповідно до JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В іншому випадку, якщо значення Виразу є нульовим, NullPointerExceptionвикидається a .

Як це виправити?

Отже, у вас є 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це недійсно, і виклик lengthметоду на ньому викидає виняток. Ми можемо бачити, що програма перестає викидати виняток, коли s.length()її видаляють із методу.

Простежте, звідки беруться ці значення

Далі перевірте, звідки походить це значення. Дотримуючись тих, що дзвонять методу, ми бачимо , що sпередається з використанням printString(name)в print()методі, і this.nameдорівнює нулю.

Простежте, де слід встановити ці значення

Де this.nameвстановлено? У setName(String)методі. З деякою налагодженням ми можемо побачити, що цей метод взагалі не викликається. Якщо метод викликався, переконайтеся, що перевіряєте порядок виклику цих методів, а встановлений метод не викликається після методу друку.

Цього достатньо, щоб дати нам рішення: додайте дзвінок до printer.setName()дзвінка printer.print().

Інші виправлення

Змінна може мати значення за замовчуваннямsetNameможе запобігти її встановленню на нуль):

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
Ви згадали про налагодження ... Як це працює? Я певний час досліджував цю тему, але нічого не можу знайти. Я впевнений, чудовий вчитель, як ти, можеш навчити мене це за секунду! Дуже дякую! :-)
Ручір Баронія

15
@RuchirBaronia Налагоджувач дозволяє переходити через програму по рядку, щоб побачити, які методи викликаються та як змінюються змінні. ІДЕ повинні мати деякі інструменти для цього. Наприклад, дивіться vogella.com/tutorials/EclipseDebugging/article.html .
fgb

15
@RuchirBaronia Ви встановлюєте точки перерви в методах навколо NullPointerExceptions, як це видно в стек-трасі, і перевіряєте значення змінних у порівнянні з тим, що ви їх очікуєте. Якщо ви знаєте, що змінна є нульовою, коли її не повинно бути, тоді ви можете встановити точки перерви навколо будь-якого коду, який змінює значення. Існують також умовні точки перерви, якими ви можете скористатися, які підкажуть, коли змінюється значення.
fgb

6
Встановлення рядкових об'єктів у порожній рядок як їх значення за замовчуванням вважається поганою практикою.
Крихітна

501

Питання: Що викликає 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". НЕ ПРАВИЛО !! Це не гоблдюк. Це стек-трек ... і він надає життєво важливу інформацію, яка допоможе вам відстежити помилку у вашому коді, якщо ви знайдете час, щоб уважно його прочитати.

Отже, давайте подивимось, що це говорить:

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:2" надає ім'я файлу вихідного класу, і воно говорить нам, що оператор, де це сталося, знаходиться у рядку 4 файлу.

Якщо порахувати рядки у файлі вище, рядок 4 - це той, який я позначив коментарем "ТУТ".

Зауважте, що у більш складному прикладі в трасі стеку NPE буде багато рядків. Але ви можете бути впевнені, що другий рядок (перший рядок "у") підкаже вам, куди було кинуто NPE 1 .

Коротше кажучи, слід стека однозначно підкаже нам, яка заява програми кинула 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)
$ 

Отже, тепер у нас є дві лінії "на". Перший для цього рядка:

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];

ми виділяємо a String[]з двома елементами , які ініціалізуютьсяnull . Після цього ми не змінили вмісту foo... так foo[1]буде і досі null.


425

Це як ви намагаєтеся отримати доступ до об'єкта, який є null. Розглянемо нижче приклад:

TypeA objA;

На даний момент ви щойно оголосили цей об'єкт, але не ініціалізувались чи створювали його . І кожного разу, коли ви намагатиметеся отримати доступ до будь-якого ресурсу чи методу, він буде кинутий NullPointerExceptionсенс.

Дивіться також цей приклад нижче:

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

1
Якщо ми дамо System.out.println (a.length ()); // NullPointerException буде кинуто, щоб пропустити це, ми можемо впоратися з блоком спробувати catch. дякую
Vijaya Varma

359

Виняток "null pointer" видається, коли програма намагається використовувати null у випадку, коли потрібен об'єкт. До них належать:

  1. Виклик методу екземпляра nullоб'єкта.
  2. Доступ або зміна поля nullоб'єкта.
  3. Приймаючи довжину nullтак, ніби це масив.
  4. Доступ до або змінення слотів nullяк би це масив.
  5. Кидання null, ніби це було значення, яке викидається.

Програми повинні запускати екземпляри цього класу, щоб вказати на інші незаконні використання nullоб'єкта.

Довідка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html


12
Нехай це буде просто, мені подобається ця відповідь, додайте це, якщо ви вважаєте правильним - Доступ до неініціалізованого атрибута об'єкта
Еміліано

5
@Emiliano - просто доступ до ініціалізованого атрибута не викликає NPE. Саме те, що ти >> робиш <<, з неініціалізованим значенням атрибута викликає NPE.
Стівен C

1
Якщо ви хочете більше випадків: 1) використання a nullяк цілі synchronizedблоку, 2) використання a nullяк цілі а switchта unboxing null.
Стівен С

333

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

321

Вже є чимало пояснень, щоб пояснити, як це відбувається і як це виправити, але слід також дотримуватися кращих практик, щоб NullPointerExceptionвзагалі уникнути s.

Дивіться також: Хороший список найкращих практик

Я б додав, що дуже важливо, добре використовувати finalмодифікатор. Використання "остаточного" модифікатора, коли це можливо на Java

Підсумок:

  1. Використовуйте finalмодифікатор для забезпечення доброї ініціалізації.
  2. Уникайте повернення нульових методів, наприклад, повернення порожніх колекцій, коли це застосовується.
  3. Використовуйте примітки @NotNullта@Nullable
  4. Збій швидко та використання тверджень, щоб уникнути поширення нульових об'єктів у всій програмі, коли вони не повинні бути нульовими.
  5. Спершу скористайтеся рівними відомому об’єкту: if("knownObject".equals(unknownObject)
  6. Віддавайте перевагу valueOf()над toString().
  7. Використовуйте безпечні StringUtilsметоди StringUtils.isEmpty(null).
  8. Використовуйте Java 8 Необов'язково як зворотне значення у методах, Додатковий клас надає рішення для подання необов'язкових значень замість нульових посилань.

4
У проектах j2ee виняток Nullpointer є дуже поширеним. У деяких випадках посилання на змінні мають нульові значення. Отже, ви повинні перевірити ініціалізацію змінної належним чином. А під час умовного оператора ви завжди повинні перевіряти, чи містить прапор чи посилання нуль чи не так: - if (flag! = 0) {ур-код, у якому використовується прапор}
Amaresh Pattanayak

14
Варто зазначити, що деякі ІДЕ (наприклад, Eclipse) пропонують автоматичні аналізи нуля на основі налаштованих анотацій (наприклад, @Nullableяк зазначено вище) та попереджають про можливі помилки. Також можна зробити висновки та генерувати такі анотації (наприклад, IntelliJ може це зробити) на основі існуючої структури коду.
Ян Чімяк

4
Перше, що потрібно зробити, - це перед тим, як використовувати об'єкт, що зводиться нанівець, слід перевірити, чи він є нульовим, використовуючи if (obj==null)його.
Lakmal Vithanage

4
ІМО, бажано уникати повернення нульових об'єктів методами, коли це можливо, і використовувати анотацію, коли нульові вхідні параметри заборонені, щоб, за контрактом, зменшити кількість ´if (obj == null) ´ у коді та покращити читабельність коду.
LG

4
Прочитайте це ... перш ніж приймати ці «найкращі практики» як істину: satisfice.com/blog/archives/27
Стівен C

316

У Java все (крім первісних типів) складається у формі класу.

Якщо ви хочете використовувати будь-який об’єкт, у вас є дві фази:

  1. Декларуйте
  2. Ініціалізація

Приклад:

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

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

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

Якщо ви не даєте розділ ініціалізації, тоді NullPointerExceptionвиникають.


3
NullPointerException часто виникає при виклику методу екземпляра. Наприклад, якщо ви оголошуєте посилання, але не вказуєте на який-небудь примірник, NullPointerException відбудеться при виклику його методу. наприклад: YourClass ref = null; // або ref = elseRef; // але elseRef не вказав жоден екземпляр ref.someMethod (); // він кине NullPointerException. Загалом виправте це таким чином: Перш ніж викликати метод, визначте, чи посилання є нульовим. наприклад: if (yourRef! = null) {yourRef.someMethod (); }
sunhang

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


315

Виняток з нульовим вказівником - це показник того, що ви використовуєте об'єкт без ініціалізації.

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

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
Тут просто недоречно використовувати слово "неініціалізований". Приклад, який ви показали, насправді "ініціалізується", і він ініціалізується з null. За неініціалізовані змінні компілятор подасть вам скарги.
Адріан Шум

2
NPE може бути показником того, що ви використовуєте неініціалізоване поле. Це може бути показником того, що ви займаєтесь іншими справами. Надмірне спрощення подібної причини не допомагає комусь вирішувати проблеми NPE ... якщо справжня причина не ця.
Стівен С

309

У Java всі змінні, які ви заявляєте, насправді є "посиланнями" на об'єкти (або примітиви), а не на самі об'єкти.

При спробі виконання одного методу об'єкта посилання просить живий об'єкт виконати цей метод. Але якщо посилання посилається на NULL (нічого, нуль, недійсність, nada), спосіб не буде виконаний. Тоді час виконання повідомляє про це, кинувши NullPointerException.

Ваше посилання "вказує" на null, таким чином "Null -> Pointer".

Об'єкт живе в просторі пам'яті VM, і єдиний спосіб отримати доступ до нього - це використання 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обидва вказують на нуль), тоді об'єкт є "недосяжним". Ми не можемо з ним працювати, тому цей об’єкт готовий до збирання сміття, і в якийсь момент VM звільнить пам’ять, що використовується цим об’єктом, і виділить інший.


280

Інший випадок 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.