Java - що таке виключення nullpointerexception і як його виправити, code q - a російська (ru)

Коли ви оголошуєте кількість посилань змінну (тобто об'єкт), ви дійсно створюєте покажчик на об'єкт. Розглянемо наступний код, в якому ви оголошуєте змінну примітивного типу int.

У цьому прикладі змінна x є int і Java ініціалізує її для 0. Коли ви призначаєте його 10 у другому рядку, ваше значення 10 записується в комірку пам'яті, на яку вказує x.

Але, коли ви намагаєтеся оголосити контрольний тип, відбувається щось інше. Візьміть наступний код:

Перший рядок оголошує змінну з ім'ям num. але вона не містить примітивного значення. Замість цього він містить покажчик (бо тип Integer є посилальним типом). Оскільки ви ще не сказали, що вказати на Java, він встановлює значення null, що означає «Я нічого не вказую».

У другому рядку ключове слово new використовується для створення (або створення) об'єкта типу Integer, а змінної-вказівником num присвоюється цей об'єкт. Тепер ви можете посилатися на об'єкт, використовуючи оператор разименованія. (крапка).

Exception про який ви просили, виникає, коли ви оголошуєте змінну, але не створюєте об'єкт. Якщо ви спробуєте разименовать num створення об'єкта, ви отримаєте NullPointerException. У самих тривіальних випадках компілятор зловить проблему і повідомить вам, що «num може не бути ініціалізувати», але іноді ви пишете код, який безпосередньо не створює об'єкт.

Наприклад, у вас може бути наступний метод:

В цьому випадку ви не створюєте об'єкт obj. скоріше припускаючи, що він був створений до doSomething методу doSomething. На жаль, цей метод можна викликати наступним чином:

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

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

Нарешті, як визначити місце розташування виключення і викликати використання Stack Trace

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

Ймовірно, найшвидший приклад коду, який я міг би придумати, щоб проілюструвати NullPointerException. буде:

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

Кинуто, коли програма намагається використовувати null в разі, коли потрібно об'єкт. До них відносяться:

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

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

Також буває, що якщо ви спробуєте використовувати нульову посилання з synchronized. це також викличе це виняток для JLS.

  • В іншому випадку, якщо значення Expression одно null, NullPointerException.

Отже, у вас є NullPointerException. як ви його виправити? Візьмемо простий приклад, який викликає NullPointerException

Визначте нульові значення

Тут ми бачимо, що виключення вибрано в рядку 13 (в методі printString). Подивіться на рядок і перевірте, які значення є нульовими, додавши інструкції ведення журналу або використовуючи відладчик. Ми з'ясували, що s одно null, і виклик методу length на ньому викликає виняток. Ми бачимо, що програма перестає кидати виняток, коли s.length () видаляється з методу.

Трасування, в якій ці значення виходять з

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

Трасування, де ці значення повинні бути встановлені

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

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

Змінна може мати значення за замовчуванням (і setName може перешкодити їй встановити значення null):

Або метод print або printString може перевіряти значення null. наприклад:

Або ви можете створити клас так, щоб name завжди мало нульове значення.

Якщо ви спробували налагодити проблему і до сих пір не маєте рішення, ви можете відправити питання для отримання додаткової довідки, але не забудьте включити те, що ви пробували досі. Як мінімум, включите stacktrace в питання і відзначте важливі номери рядків в коді. Також спробуйте спочатку спростити код (див. SSCCE).

Питання: Що викликає NullPointerException?

Як ви повинні знати, типи Java діляться на примітивні типи (boolean. Int т. Д.) І посилальні типи. Типи посилань в Java дозволяють використовувати спеціальне значення null яке є способом Java, що говорять «no object».

NullPointerException під час виконання, коли ваша програма намагається використовувати null як якби вона була реальною посиланням. Наприклад, якщо ви пишете це:

Оператор, позначений як «ТУТ», спробує запустити метод length () в null посиланням, і це викличе NullPointerException.

Існує багато способів використання null значення, яке призведе до NullPointerException. Якщо факт, єдине, що ви можете зробити з null без виникнення NPE, це:

Питання: Як мені дізнатися стек NPE?

Припустимо, що я компілює і запускаю програму вище:

Перше спостереження: компіляція завершується успішно! Проблема в програмі НЕ є помилкою компіляції. Це помилка часу виконання. (Деякі IDE можуть попередити, що ваша програма завжди буде генерувати виняток. Але стандартний javac компілятор не робить цього.)

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

Тому давайте подивимося, що говорить:

Перший рядок трасування стека повідомляє вам кілька речей:

  • Він повідомляє вам ім'я потоку Java, в якому було вибрано виняток. Для простої програми з одним потоком (як цей) вона буде «основний». Давайте рухатися далі.
  • Він повідомляє вам повне ім'я виключення, яке було вибрано; Тобто java.lang.NullPointerException.
  • Якщо у виключенні є пов'язане повідомлення про помилку, це буде виводитися після імені виключення. В цьому відношенні NullPointerException незвично, тому що воно рідко має повідомлення про помилку.

Другий рядок є найбільш важливою при діагностиці NPE.

Це говорить нам про кілька речей:

  • «У Test.main» говориться, що ми були в main методі класу Test.
  • «Test.java:4» дає вихідне ім'я файлу для класу, і він повідомляє нам, що оператор, де це сталося, знаходиться в рядку 4 файлу.

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

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

1 - Не зовсім вірно. Є речі, які називаються вкладеними винятками.

Питання: Як визначити причину виключення NPE в моєму коді?

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

Давайте спочатку проілюструємо простим прикладом (див. Вище). Ми починаємо з розгляду рядки, про яку нам розповіли stacktrace, де відбувається NPE:

Як це може викликати NPE?

Насправді існує тільки один спосіб: це може статися тільки в тому випадку, якщо foo має значення null. Потім ми намагаємося запустити метод length () на null і. BANG!

Але (я чув, ви говорите), що, якщо NPE був кинутий в виклик методу length ().

Добре, якщо це станеться, стек буде виглядати по-іншому. У першому рядку «at» буде вказано, що виключення було вибрано в деякій рядку в класі java.lang.String. а рядок 4 Test.java була б другим рядком «at».

Отже, звідки це сталося? У цьому випадку це очевидно, і очевидно, що нам потрібно зробити, щоб виправити це. (Призначте нульове значення для foo)

Добре, так що давайте спробуємо трохи більш хитрий приклад. Для цього буде потрібно певний логічний висновок.

Отже, тепер у нас є 2 рядки «на лінії». Перша - для цього рядка:

А друга - для цього рядка:

Отже, дивлячись на першу сходинку, як це може викликати NPE? Фактично, є два способи:

  • Якщо значення bar одно null тоді bar [pos] викликатиме NPE.
  • Якщо значення bar [pos] одно null то виклик length () на ньому викличе NPE.

Тому нам потрібно з'ясувати, який з цих сценаріїв пояснює, що відбувається насправді. Давайте почнемо з вивчення першого:

Звідки береться bar. Це параметр виклику методу test. і якщо ми подивимося, як був викликаний test. ми можемо бачити, що він виходить з статичної змінної foo. І ми ясно бачимо, що ми ініціалізували foo на нульове значення. Цього достатньо, щоб умовно відкинути це пояснення. (Теоретично, щось ще може змінити foo на null. Але тут цього не відбувається).

Так що щодо другого сценарію? Добре, ми бачимо, що pos дорівнює 1. тому це означає, що foo [1] повинен бути null. Це можливо?

Справді! І в цьому проблема. Коли ми инициализируем так:

Ми виділяємо String [] з двома елементами, які не започатковано null. І тоді ми не змінили вміст foo. так що foo [1] все одно буде null.

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

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

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

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

Покажчик NULL - це той, який вказує на нікуди. Коли ви розшукуєте покажчик p. ви говорите «дайте мені дані в місці, яке зберігається в« p ». Коли p є нульовим покажчиком, місце розташування, яке зберігається в p. nowhere зазначено, ви говорите «дайте мені дані в місці« Ніде ». Очевидно, він не може цього зробити, тому він NULL pointer exception.

Загалом, це тому, що щось не було правильно ініціалізувати.

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

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

У Java всі речі знаходяться в формі класу.

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

Те ж саме для концепції Array

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

Якщо ви не дали розділ NullpointerException виникає NullpointerException.

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

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

Нижче код дає вам виняток нульового покажчика.

Оскільки ви використовуєте Obj_Student але ви забули його форматувати, як показано на малюнку нижче.

Об'єкт живе в просторі пам'яті VM, і єдиний доступ до нього - this використання this посилань. Візьмемо такий приклад:

Інша поява NullPointerException виникає, коли оголошується масив об'єктів, а потім відразу ж намагається розгледіти елементи всередині нього.

Цей конкретний NPE можна уникнути, якщо порядок порівняння буде скасований; А саме, використовувати .equals на гарантованому ненулевом об'єкті.

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

Ви повинні ініціалізувати елементи в масиві перед тим, як отримати доступ до них або розблокувати їх.