Что из себя представляет исключение Null Pointer Exception (java.lang.NullPointerException
) и почему оно может происходить?
Какие методы и средства использовать, чтобы определить причину возникновения этого исключения, приводящего к преждевременному прекращению работы приложения?
Перевод вопроса «What is a Null Pointer Exception, and how do I fix it?» @Ziggy.
Когда вы объявляете переменную ссылочного типа, на самом деле вы создаете ссылку на объект данного типа. Рассмотрим следующий код для объявления переменной типа int:
int x;
x = 10;
В этом примере переменная x
имеет тип int
и Java инициализирует её как 0. Когда вы присвоите переменной значение 10 (вторая строка), это значение сохранится в ячейке памяти, на которую ссылается x
.
Но когда вы объявляете ссылочный тип, процесс выглядит иначе. Посмотрим на следующий код:
Integer num;
num = new Integer(10);
В первой строке объявлена переменная num
, ее тип не относится к встроенному, следовательно, значением является ссылка (тип этой переменной, Integer
, является ссылочным типом). Поскольку вы еще не указали, на что собираетесь ссылаться, Java присвоит переменной значение Null
, подразумевая «Я ни на что не ссылаюсь».
Во второй строке, ключевое слово new
используется для создания объекта типа Integer
. Этот объект имеет адрес в памяти, который присваивается переменной num
. Теперь, с помощью переменной num
вы можете обратиться к объекту используя оператора разыменования .
.
Исключение, о котором вы говорите в вопросе, возникает, если вы объявили переменную, но не создали объект, то есть если вы попытаетесь разыменовать num
до того, как создали объект, вы получите NullPointerException
. В самом простом случае, компилятор обнаружит проблему и сообщит, что
num
may not have been initialized
Что говорит: «возможно, переменная num
не инициализирована».
Иногда исключение вызвано именно тем, что объект действительно не был создан. К примеру, у вас может быть следующая функция:
public void doSomething(Integer num){
// Работаем с num
}
В этом случае создание объекта (переменная num
) лежит на вызывающем коде, то есть вы предполагаете, что он был создан ранее – до вызова метода doSomething
. К сожалению, следующий вызов метода вполне возможен:
doSomething(null);
В этом случае значение переменной num
будет null
. Лучшим способом избежать данного исключения будет проверка на равенство нулю. Как результат, функция doSomething
должна быть переписана следующим образом:
public void doSomething(Integer num){
if (num != null) {
// Работаем с num
}
}
Как альтернативный вариант предыдущему примеру вы можете сообщить вызывающему коду, что метод был вызван с неверными параметрами, например, с помощью IllegalArgumentException
.
public void doSomething(Integer num){
if (num == null)
throw new IllegalArgumentException("Num не должен быть null");
// Работаем с num
}
Также, обратите внимание на вопрос «Что такое stack trace и как с его помощью находить ошибки при разработке приложений?».
Перевод ответа «What is a Null Pointer Exception, and how do I fix it?» @Vincent Ramdhanie.
В java8 был добавлен класс Optional
для предотвращения NullPointerException
, хотя использовать его по назначению не всегда удается правильно. Вот ссылка с рекомендациями как использовать Optional
Типичное решение с проверкой на null
Article article = getFirstJavaArticle();
if (article == null) {
article = fetchLatestArticle();
}
А теперь решение с использованием Optional<T>
. Представим что getFirstJavaArticle()
возвращает Optional<Article>
getFirstJavaArticle()
.orElseGet(this::fetchLatestArticle);
Решение с Optional
позволяет нам избежать кода с проверками на null
и работать в функциональном стиле. Стоит заметить что Optional не является панацеей от всех бед
Так же для предотвращения NullPointerException
используется паттерн
NullObject
Абстрактный Entity class
public abstract class AbstractCustomer {
protected String name;
public abstract String getName();
}
Реальная реализация
public class RealCustomer extends AbstractCustomer {
public RealCustomer(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}
Реализация NullObject
public class NullCustomer extends AbstractCustomer {
@Override
public String getName() {
return "Not Available in Customer Database";
}
}
Использование (как пример)
public class CustomerFactory {
public static final String[] names = {"Rob", "Joe", "Julie"};
public static AbstractCustomer getCustomer(String name){
for (int i = 0; i < names.length; i++) {
if (names[i].equalsIgnoreCase(name)){
return new RealCustomer(name);
}
}
return new NullCustomer();
}
}
Вместо возвращения null
мы возвращаем экземпляр класс NullCustomer
и не падаем с NullPointerException
. Но как мне кажется присутствует небольшой Overhead: необходимо создавать иерархию наследования.
Это Exception , который встречается тогда, когда ты ссылаешься на объект, который = null(не инициализирован) или пытаешься вызвать методы/обратиться к переменным объекта, который = null. Решение этой проблемы:
Проверка на null, if(Твой объект==null){//Your code}
try{}catch(NullPointerException e){//Your code}
Когда тебе выбрасывает нулл,то обычно указывает "иерархию строчек"(что, как вызывалось, что привело тебя к нулл). И там ты можешь посмотреть, где у тебя объект не инициализирован, там и можно сразу исправить ошибку.
Может встречаться в массивах/списках, когда вызываешь объект под номер n, при том, что объекта туда предварительно не записан. Вроде, все))
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
В консоль выводятся куча байт, как из modifiedSentence получить строку?
Пишу прогу, которая принимает строку(только буквы, никаких знаков препинания и цифр) и целое число, насколько передвинуть символы сроки по ASCII таблицеНичего...
Как создать SSL соединение к postgresql из Java swing?