Відповіді:
Коли ви оголошуєте опорну змінну (тобто об'єкт), ви дійсно створюєте вказівник на об’єкт. Розглянемо наступний код, де ви оголошуєте змінну примітивного типу 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
int a=bNPE при використанні автобоксингу: може кинути NPE, якщо b - an Integer. Є випадки, коли це заплутано для налагодження.
NullPointerExceptionпроблем у вашому коді є використання @Nullableта @NotNullпримітки. Наступна відповідь має більше інформації щодо цього. Хоча ця відповідь специфічно стосується IntelliJ IDE, вона також застосовна до інших інструментів, як це стосується коментарів з цих коментарів. (До речі, мені не дозволяють редагувати цю відповідь безпосередньо, можливо, автор може її додати?)
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, який вказує на неправильне місце пам'яті. Нульовий покажчик буквально нікуди не вказує , що тонко відрізняється, ніж вказує на місце, яке, можливо, недійсне.)
nullперед її використанням, як це . За допомогою локальних змінних компілятор вловлює цю помилку, але в цьому випадку це не так. Може, це стане корисним доповненням до вашої відповіді?
Гарне місце для початку - 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 ).
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.Припустимо, що я компілюю та запускаю програму вище:
$ 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.lang.NullPointerException.NullPointerExceptionє незвичним у цьому відношенні, оскільки він рідко має повідомлення про помилку.Другий рядок є найважливішим у діагностиці NPE.
at Test.main(Test.java:4)
Це говорить нам про ряд речей:
mainметоді Testкласу.Якщо порахувати рядки у файлі вище, рядок 4 - це той, який я позначив коментарем "ТУТ".
Зауважте, що у більш складному прикладі в трасі стеку NPE буде багато рядків. Але ви можете бути впевнені, що другий рядок (перший рядок "у") підкаже вам, куди було кинуто NPE 1 .
Коротше кажучи, слід стека однозначно підкаже нам, яка заява програми кинула NPE.
1 - Не зовсім правда. Є речі, які називаються вкладеними винятками ...
Це важка частина. Коротка відповідь полягає у застосуванні логічного висновку до доказів, наданих слідом стека, вихідним кодом та відповідною документацією 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.
Це як ви намагаєтеся отримати доступ до об'єкта, який є null. Розглянемо нижче приклад:
TypeA objA;
На даний момент ви щойно оголосили цей об'єкт, але не ініціалізувались чи створювали його . І кожного разу, коли ви намагатиметеся отримати доступ до будь-якого ресурсу чи методу, він буде кинутий NullPointerExceptionсенс.
Дивіться також цей приклад нижче:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
Виняток "null pointer" видається, коли програма намагається використовувати null у випадку, коли потрібен об'єкт. До них належать:
nullоб'єкта.nullоб'єкта.nullтак, ніби це масив.nullяк би це масив.null, ніби це було значення, яке викидається.Програми повинні запускати екземпляри цього класу, щоб вказати на інші незаконні використання nullоб'єкта.
Довідка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
nullяк цілі synchronizedблоку, 2) використання a nullяк цілі а switchта unboxing null.
nullПокажчик один , який вказує в нікуди. Коли ви разименованія покажчика p, ви говорите , «дайте мені дані на місці , що зберігається в" р ". Коли pце nullпокажчик, розташування зберігається в pце nowhere, ви говорите , " дайте мені дані на місці "ніде". Очевидно, він не може цього зробити, тому кидає а null pointer exception.
Загалом, це тому, що щось не було ініціалізовано належним чином.
NULLпишеться як nullу java. І справа в залежності від регістру.
Вже є чимало пояснень, щоб пояснити, як це відбувається і як це виправити, але слід також дотримуватися кращих практик, щоб NullPointerExceptionвзагалі уникнути s.
Дивіться також: Хороший список найкращих практик
Я б додав, що дуже важливо, добре використовувати finalмодифікатор.
Використання "остаточного" модифікатора, коли це можливо на Java
Підсумок:
finalмодифікатор для забезпечення доброї ініціалізації.@NotNullта@Nullableif("knownObject".equals(unknownObject)valueOf()над toString().StringUtilsметоди StringUtils.isEmpty(null).@Nullableяк зазначено вище) та попереджають про можливі помилки. Також можна зробити висновки та генерувати такі анотації (наприклад, IntelliJ може це зробити) на основі існуючої структури коду.
if (obj==null)його.
У Java все (крім первісних типів) складається у формі класу.
Якщо ви хочете використовувати будь-який об’єкт, у вас є дві фази:
Приклад:
Object object;object = new Object();Те саме для концепції масиву:
Item item[] = new Item[5];item[0] = new Item();Якщо ви не даєте розділ ініціалізації, тоді NullPointerExceptionвиникають.
Виняток з нульовим вказівником - це показник того, що ви використовуєте об'єкт без ініціалізації.
Наприклад, нижче наведено студентський клас, який використовуватиме його у нашому коді.
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 всі змінні, які ви заявляєте, насправді є "посиланнями" на об'єкти (або примітиви), а не на самі об'єкти.
При спробі виконання одного методу об'єкта посилання просить живий об'єкт виконати цей метод. Але якщо посилання посилається на 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 звільнить пам’ять, що використовується цим об’єктом, і виділить інший.
Інший випадок 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));
}
Optionalповинен був повернути нуль. Ключове слово добре. Знання, як уберегтися від цього, є критичним. Це пропонує одне поширене явище і способи його пом'якшення.