Что из себя представляет исключение Null Pointer Exception (java.lang.NullPointerException) и почему оно может происходить?
Какие методы и средства использовать, чтобы определить причину возникновения этого исключения, приводящего к преждевременному прекращению работы приложения?
Перевод вопроса «What is a Null Pointer Exception, and how do I fix it?» @Ziggy.
Ответы
Ответ 1
Когда вы объявляете переменную ссылочного типа, на самом деле вы создаете ссылк
на объект данного типа. Рассмотрим следующий код для объявления переменной типа 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?» @Vincen
Ramdhanie.
Ответ 2
В java8 был добавлен класс Optional для предотвращения NullPointerException, хот
использовать его по назначению не всегда удается правильно. Вот ссылка с рекомендациями как использовать Optional
Типичное решение с проверкой на null
Article article = getFirstJavaArticle();
if (article == null) {
article = fetchLatestArticle();
}
А теперь решение с использованием Optional. Представим что getFirstJavaArticle() возвращает Optional
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: необходимо создавать иерархию наследования.
Ответ 3
Это Exception , который встречается тогда, когда ты ссылаешься на объект, которы
= null(не инициализирован) или пытаешься вызвать методы/обратиться к переменным объекта, который = null. Решение этой проблемы:
Проверка на null, if(Твой объект==null){//Your code}
try{}catch(NullPointerException e){//Your code}
Когда тебе выбрасывает нулл,то обычно указывает "иерархию строчек"(что, как вызывалось
что привело тебя к нулл). И там ты можешь посмотреть, где у тебя объект не инициализирован, там и можно сразу исправить ошибку.
Может встречаться в массивах/списках, когда вызываешь объект под номер n, при том, что объекта туда предварительно не записан.
Вроде, все))
Ответ 4
Советую использовать Optional для предотвращения NPE. Этот класс был введене в java 8.
К примеру
@Entity
public class Car {
@Id
@GeneratedValue
private Long id;
@Column(name = "name")
@NotNull
private String name;
}
Репозиторий:
public interface CarRepository extends JpaRepository {
Optional findById(Long id);
}
Сервис:
@Service
public class CarService {
private CarRepository carRepository;
@Autowired
public CarService(CarRepository carRepository) {
this.carRepository = carRepository;
}
public Car findById(Long id) {
Car car = carRepository.findById(id).orElseThrow(() ->
new RuntimeException("Requested entity was not found"));
car.getHolder().setPassword(null);
return car;
}
}
Контроллер:
@RestController
@RequestMapping("/api")
public class CarController {
private CarService carService;
@Autowired
public CarController(CarService carService) {
this.carService = carService;
}
@RequestMapping(value = "/cars/{id}", method = RequestMethod.GET)
public ResponseEntity getCar(@PathVariable Long id) {
try {
Car carToFind = carService.findById(id);
return ResponseEntity.status(HttpStatus.OK).body(carToFind);
} catch (Exception ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
}
}
В таком случае, если репозиторий не сможет найти машину, он не выкинет NPE.
Optional помогает избежать проверок на null и сократить код и улучшить его читаемостью.
Комментариев нет:
Отправить комментарий