Ответы:
Когда вы объявляете ссылочную переменную (т.е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа 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. Фактически, единственные вещи, которые вы можете сделать с помощью nullNPE:
==или !=операторов, или 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и@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");
}
}
}
На яве все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не на сами объекты.
Когда вы пытаетесь выполнить один метод объекта, ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на 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должен был возвращать ноль. Ключевое слово в порядке. Знание того, как защититься, очень важно. Это предлагает один общий случай этого и способы смягчить это.