Ответы:
Когда вы объявляете ссылочную переменную (т.е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа 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 динамически
int a=b
могут бросить NPE, если b является Integer
. Есть случаи, когда это сбивает с толку для отладки.
NullPointerException
проблем в вашем коде является использование @Nullable
и @NotNull
аннотации. Следующий ответ имеет больше информации об этом. Хотя этот ответ относится конкретно к IntelliJ IDE, он также применим к другим инструментам, как это описано в комментариях. (Кстати, мне не разрешено редактировать этот ответ напрямую, возможно, автор может добавить его?)
NullPointerException
Это исключения, которые возникают, когда вы пытаетесь использовать ссылку, которая указывает на отсутствие места в памяти (ноль), как если бы оно ссылалось на объект. Вызов метода с нулевой ссылкой или попытка доступа к полю с нулевой ссылкой вызовет a NullPointerException
. Это наиболее распространенные, но другие способы перечислены наNullPointerException
странице javadoc.
Вероятно, самый быстрый пример кода, который я мог бы придумать для иллюстрации, был NullPointerException
бы:
public class Example {
public static void main(String[] args) {
Object obj = null;
obj.hashCode();
}
}
На первой линии внутри main
я явно устанавливаю Object
ссылку obj
на null
. Это означает, что у меня есть ссылка, но она не указывает ни на какой объект. После этого я пытаюсь обработать ссылку так, как будто она указывает на объект, вызывая метод для него. Это приводит кNullPointerException
что в местоположении, на которое указывает ссылка, нет кода для выполнения.
(Это техническая проблема, но я думаю, что стоит упомянуть: ссылка, указывающая на ноль, не совпадает с указателем C, указывающим на недопустимое расположение в памяти. Нулевой указатель буквально никуда не указывает , что несколько отличается от указывая на местоположение, которое оказывается недействительным.)
null
перед ее использованием, например, так . С локальными переменными компилятор поймает эту ошибку, но в этом случае это не так. Может быть, это будет полезным дополнением к вашему ответу?
Хорошее место для начала - JavaDocs . У них есть это покрыто:
Брошенный, когда приложение пытается использовать нуль в случае, когда объект требуется. Это включает:
- Вызов метода экземпляра нулевого объекта.
- Доступ или изменение поля нулевого объекта.
- Принимая длину нуля, как если бы это был массив.
- Доступ или изменение пустых слотов, как если бы это был массив.
- Бросить ноль, как если бы это было значение Throwable.
Приложения должны генерировать экземпляры этого класса, чтобы указать на другое незаконное использование нулевого объекта.
Это также тот случай, когда вы попытаетесь использовать нулевую ссылку с synchronized
, это также сгенерирует это исключение для JLS :
SynchronizedStatement: synchronized ( Expression ) Block
- В противном случае, если значение Expression равно нулю, генерируется 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
методе). Посмотрите на строку и проверьте, какие значения являются нулевыми, добавив операторы регистрации или используя отладчик . Мы выясняем, что s
это null, и вызов length
метода для него вызывает исключение. Мы видим, что программа перестает генерировать исключение, когдаs.length()
удаляется из метода.
Проследите, откуда берутся эти значения
Затем проверьте, откуда это значение. Следуя указаниям метода, мы видим, что s
передается printString(name)
в print()
метод, иthis.name
имеет значение null.
Трассировка, где эти значения должны быть установлены
Где this.name
установлен? В setName(String)
методе. После некоторой дополнительной отладки мы видим, что этот метод вообще не вызывается. Если метод был вызван, обязательно проверьте порядок вызова этих методов, и метод set не вызывается после метода print.
Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName()
перед вызовом printer.print()
.
Переменная может иметь значение по умолчанию (и setName
может помешать ей иметь значение null):
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
генерируется во время выполнения всякий раз, когда ваша программа пытается использовать a, 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 будет много строк. Но вы можете быть уверены, что вторая строка (первая строка «at») скажет вам, куда был брошен NPE 1 .
Короче говоря, трассировка стека однозначно скажет нам, какой оператор программы бросил NPE.
1 - не совсем верно. Есть вещи, называемые вложенными исключениями ...
Это сложная часть. Краткий ответ - применить логический вывод к свидетельству, предоставленному трассировкой стека, исходному коду и соответствующей документации API.
Давайте сначала проиллюстрируем на простом примере (выше). Мы начнем с того, что посмотрим на строку, которую трассировка стека сказала нам, где произошло NPE:
int length = foo.length(); // HERE
Как это может бросить NPE?
На самом деле, есть только один путь: это может произойти, только если foo
имеет значение null
. Затем мы пытаемся запустить length()
метод null
и ... Взрыв!
Но (я слышу, вы говорите), что если бы NPE был брошен внутри length()
вызова метода?
Ну, если бы это произошло, трассировка стека выглядела бы иначе. Первая строка «at» будет означать, что исключение было сгенерировано в некоторой строке в java.lang.String
классе и в строке 4Test.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.com/javase/8/docs/api/java/lang/NullPointerException.html
null
цели как цели synchronized
, 2) использование null
цели как цели switch
, и распаковка null
.
null
Указатель один , который указывает в никуда. Когда вы разыменования указателя p
, вы говорите , «дайте мне данные на месте , хранящегося в„р“. Когда p
это null
указатель, расположение хранится в p
это nowhere
, вы говорите , „дайте мне данные на месте "нигде“. Очевидно, он не может этого сделать, поэтому он выбрасывает null pointer exception
.
В общем, это потому, что что-то не было правильно инициализировано.
NULL
написано как null
в Java. И это чувствительная к регистру вещь.
Уже есть много объяснений, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать передовым методам, чтобы NullPointerException
вообще избежать их .
Смотрите также: хороший список лучших практик
Я бы добавил, что очень важно, хорошо использовать final
модификатор.
Использование модификатора 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");
}
}
}
На яве все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не на сами объекты.
Когда вы пытаетесь выполнить один метод объекта, ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, ноль, void, nada), то метод не выполняется. Затем среда выполнения сообщит вам об этом, выдав исключение NullPointerException.
Ваша ссылка «указывает» на ноль, то есть «Нуль -> Указатель».
Объект живет в пространстве памяти ВМ, и единственный способ получить к нему доступ - использовать 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
оба указывают на ноль), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сборке мусора, и в какой-то момент виртуальная машина освободит память, используемую этим объектом, и выделит другой.
Другое возникновение a 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
должен был возвращать ноль. Ключевое слово в порядке. Знание того, как защититься, очень важно. Это предлагает один общий случай этого и способы смягчить это.