Відповіді:
Коли ви оголошуєте опорну змінну (тобто об'єкт), ви дійсно створюєте вказівник на об’єкт. Розглянемо наступний код, де ви оголошуєте змінну примітивного типу 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=b
NPE при використанні автобоксингу: може кинути NPE, якщо b - an Integer
. Є випадки, коли це заплутано для налагодження.
NullPointerException
проблем у вашому коді є використання @Nullable
та @NotNull
примітки. Наступна відповідь має більше інформації щодо цього. Хоча ця відповідь специфічно стосується IntelliJ IDE, вона також застосовна до інших інструментів, як це стосується коментарів з цих коментарів. (До речі, мені не дозволяють редагувати цю відповідь безпосередньо, можливо, автор може її додати?)
NullPointerException
s - це винятки, які виникають при спробі використовувати посилання, яке вказує на відсутність місця в пам'яті (null), як ніби воно посилається на об'єкт. Виклик методу з нульовою посиланням або спробу доступу до поля нульової посилання викликає a NullPointerException
. Це найпоширеніші, але інші способи перераховані на сторінці NullPointerException
javadoc.
Можливо, найшвидший приклад коду, який я міг би придумати для ілюстрації, був 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
та@Nullable
if("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
повинен був повернути нуль. Ключове слово добре. Знання, як уберегтися від цього, є критичним. Це пропонує одне поширене явище і способи його пом'якшення.