Ошибка браузера java lang nullpointerexception
Эта простая статья скорее для начинающих разработчиков Java, хотя я нередко вижу и опытных коллег, которые беспомощно глядят на stack trace, сообщающий о NullPointerException (сокращённо NPE), и не могут сделать никаких выводов без отладчика. Разумеется, до NPE своё приложение лучше не доводить: вам помогут null-аннотации, валидация входных параметров и другие способы. Но когда пациент уже болен, надо его лечить, а не капать на мозги, что он ходил зимой без шапки.
- Его кинули с помощью throw
- Кто-то кинул null с помощью throw
- Кто-то пытается обратиться по null-ссылке
- Вызов нестатического метода класса
- Обращение (чтение или запись) к нестатическому полю
- Обращение (чтение или запись) к элементу массива
- Чтение length у массива
- Неявный вызов метода valueOf при анбоксинге (unboxing)
Рассмотрим такой код:
Откуда-то был вызван метод handle с какими-то параметрами, и вы получили:
В чём причина исключения — в f, d или d.val? Нетрудно заметить, что f в этой строке вообще не читается, так как метод format статический. Конечно, обращаться к статическому методу через экземпляр класса плохо, но такой код встречается (мог, например, появиться после рефакторинга). Так или иначе значение f не может быть причиной исключения. Если бы d был не null, а d.val — null, тогда бы исключение возникло уже внутри метода format (в девятой строчке). Аналогично проблема не могла быть внутри метода getValue, даже если бы он был сложнее. Раз исключение в пятнадцатой строчке, остаётся одна возможная причина: null в параметре d.
Вот другой пример:
Снова вызываем метод handle и получаем
Теперь метод format нестатический, и f вполне может быть источником ошибки. Зато s не может быть ни под каким соусом: в девятой строке уже было обращение к s. Если бы s было null, исключение бы случилось в девятой строке. Просмотр логики кода перед исключением довольно часто помогает отбросить некоторые варианты.
С логикой, конечно, надо быть внимательным. Предположим, условие в девятой строчке было бы написано так:
Теперь в самой строчке обращения к полям и методам s нету, а метод equals корректно обрабатывает null, возвращая false, поэтому в таком случае ошибку в двенадцатой строке мог вызвать как f, так и s. Анализируя вышестоящий код, уточняйте в документации или исходниках, как используемые методы и конструкции реагируют на null. Оператор конкатенации строк +, к примеру, никогда не вызывает NPE.
Вот такой код (здесь может играть роль версия Java, я использую Oracle JDK 1.7.0.45):
Вызываем метод dump, получаем такое исключение:
В параметре pw не может быть null, иначе нам не удалось бы войти в метод print. Возможно, null в obj? Легко проверить, что pw.print(null) выводит строку «null» без всяких исключений. Пойдём с конца. Исключение случилось здесь:
В строке 473 возможна только одна причина NPE: обращение к методу length строки s. Значит, s содержит null. Как так могло получиться? Поднимемся по стеку выше:
В метод write передаётся результат вызова метода String.valueOf. В каком случае он может вернуть null?
Единственный возможный вариант — obj не null, но obj.toString() вернул null. Значит, ошибку надо искать в переопределённом методе toString() нашего объекта MyObject. Заметьте, в stack trace MyObject вообще не фигурировал, но проблема именно там. Такой несложный анализ может сэкономить кучу времени на попытки воспроизвести ситуацию в отладчике.
Не стоит забывать и про коварный автобоксинг. Пусть у нас такой код:
И такое исключение:
На первый взгляд единственный вариант — это null в параметре obj. Но следует взглянуть на класс MyContainer:
Мы видим, что getCount() возвращает Integer, который автоматически превращается в int именно в третьей строке TestNPE.java, а значит, если getCount() вернул null, произойдёт именно такое исключение, которое мы видим. Обнаружив класс, подобный классу MyContainer, посмотрите в истории системы контроля версий, кто его автор, и насыпьте ему крошек под одеяло.
Помните, что если метод принимает параметр int, а вы передаёте Integer null, то анбоксинг случится до вызова метода, поэтому NPE будет указывать на строку с вызовом.
В заключение хочется пожелать пореже запускать отладчик: после некоторой тренировки анализ кода в голове нередко выполняется быстрее, чем воспроизведение трудноуловимой ситуации.
Что из себя представляет исключение Null Pointer Exception ( java.lang.NullPointerException ) и почему оно может происходить?
Какие методы и средства использовать, чтобы определить причину возникновения этого исключения, приводящего к преждевременному прекращению работы приложения?
4 ответа 4
Когда вы объявляете переменную ссылочного типа, на самом деле вы создаете ссылку на объект данного типа. Рассмотрим следующий код для объявления переменной типа int:
В этом примере переменная x имеет тип int и Java инициализирует её как 0. Когда вы присвоите переменной значение 10 (вторая строка), это значение сохранится в ячейке памяти, на которую ссылается x .
Но когда вы объявляете ссылочный тип, процесс выглядит иначе. Посмотрим на следующий код:
В первой строке объявлена переменная num , ее тип не относится к встроенному, следовательно, значением является ссылка (тип этой переменной, Integer , является ссылочным типом). Поскольку вы еще не указали, на что собираетесь ссылаться, Java присвоит переменной значение Null , подразумевая «Я ни на что не ссылаюсь».
Во второй строке, ключевое слово new используется для создания объекта типа Integer . Этот объект имеет адрес в памяти, который присваивается переменной num . Теперь, с помощью переменной num вы можете обратиться к объекту используя оператора разыменования . .
Исключение, о котором вы говорите в вопросе, возникает, если вы объявили переменную, но не создали объект, то есть если вы попытаетесь разыменовать num до того, как создали объект, вы получите NullPointerException . В самом простом случае, компилятор обнаружит проблему и сообщит, что
num may not have been initialized
Что говорит: «возможно, переменная num не инициализирована».
Иногда исключение вызвано именно тем, что объект действительно не был создан. К примеру, у вас может быть следующая функция:
В этом случае создание объекта (переменная num ) лежит на вызывающем коде, то есть вы предполагаете, что он был создан ранее – до вызова метода doSomething . К сожалению, следующий вызов метода вполне возможен:
В этом случае значение переменной num будет null . Лучшим способом избежать данного исключения будет проверка на равенство нулю. Как результат, функция doSomething должна быть переписана следующим образом:
Как альтернативный вариант предыдущему примеру вы можете сообщить вызывающему коду, что метод был вызван с неверными параметрами, например, с помощью IllegalArgumentException .
Что это за ошибка java.lang.nullpointerexception
Что в отношении обычных пользователей, то появление ошибки java.lang.nullpointerexception у вас на ПК сигнализирует, что у вас что-то не так с функционалом пакетом Java на вашем компьютере, или что программа (или онлайн-приложение), работающие на Java, функционируют не совсем корректно. Если у вас возникает проблема, при которой Java апплет не загружен, рекомендую изучить материал по ссылке.
Как избавиться от ошибки java.lang.nullpointerexception? Способы борьбы с проблемой можно разделить на две основные группы – для пользователей и для разработчиков.
Для пользователей
Если вы встретились с данной ошибкой во время запуска (или работы) какой-либо программы (особенно это касается minecraft), то рекомендую выполнить следующее:
Для разработчиков
Разработчикам стоит обратить внимание на следующее:
- Вызывайте методы equals(), а также equalsIgnoreCase() в известной строке литерала, и избегайте вызова данных методов у неизвестного объекта;
- Вместо toString() используйте valueOf() в ситуации, когда результат равнозначен;
- Применяйте null-безопасные библиотеки и методы;
- Старайтесь избегать возвращения null из метода, лучше возвращайте пустую коллекцию;
- Применяйте аннотации @Nullable и @NotNull;
- Не нужно лишней автоупаковки и автораспаковки в создаваемом вами коде, что приводит к созданию ненужных временных объектов;
- Регламентируйте границы на уровне СУБД;
- Правильно объявляйте соглашения о кодировании и выполняйте их.
Заключение
This question's answers are a community effort. Edit existing answers to improve this post. It is not currently accepting new answers or interactions.
What are Null Pointer Exceptions ( java.lang.NullPointerException ) and what causes them?
What methods/tools can be used to determine the cause so that you stop the exception from causing the program to terminate prematurely?
12 Answers 12
When you declare a reference variable (i.e., an object), you are really creating a pointer to an object. Consider the following code where you declare a variable of primitive type int :
In this example, the variable x is an int and Java will initialize it to 0 for you. When you assign the value of 10 on the second line, your value of 10 is written into the memory location referred to by x .
But, when you try to declare a reference type, something different happens. Take the following code:
The first line declares a variable named num , but it does not actually contain a primitive value yet. Instead, it contains a pointer (because the type is Integer which is a reference type). Since you have not yet said what to point to, Java sets it to null , which means "I am pointing to nothing".
In the second line, the new keyword is used to instantiate (or create) an object of type Integer , and the pointer variable num is assigned to that Integer object.
The NullPointerException (NPE) occurs when you declare a variable but did not create an object and assign it to the variable before trying to use the contents of the variable (called dereferencing). So you are pointing to something that does not actually exist.
Dereferencing usually happens when using . to access a method or field, or using [ to index an array.
If you attempt to dereference num before creating the object you get a NullPointerException . In the most trivial cases, the compiler will catch the problem and let you know that " num may not have been initialized ," but sometimes you may write code that does not directly create the object.
For instance, you may have a method as follows:
In which case, you are not creating the object obj , but rather assuming that it was created before the doSomething() method was called. Note, it is possible to call the method like this:
In which case, obj is null , and the statement obj.myMethod() will throw a NullPointerException .
If the method is intended to do something to the passed-in object as the above method does, it is appropriate to throw the NullPointerException because it's a programmer error and the programmer will need that information for debugging purposes.
In addition to NullPointerException s thrown as a result of the method's logic, you can also check the method arguments for null values and throw NPEs explicitly by adding something like the following near the beginning of a method:
Note that it's helpful to say in your error message clearly which object cannot be null . The advantage of validating this is that 1) you can return your own clearer error messages and 2) for the rest of the method you know that unless obj is reassigned, it is not null and can be dereferenced safely.
Alternatively, there may be cases where the purpose of the method is not solely to operate on the passed in object, and therefore a null parameter may be acceptable. In this case, you would need to check for a null parameter and behave differently. You should also explain this in the documentation. For example, doSomething() could be written as:
What methods/tools can be used to determine the cause so that you stop the exception from causing the program to terminate prematurely?
Now Java 14 has added a new language feature to show the root cause of NullPointerException. This language feature has been part of SAP commercial JVM since 2006.
In Java 14, the following is a sample NullPointerException Exception message:
in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.List.size()" because "list" is null
I've had instances of our Java code catch a NullPointerException , but when I try to log the StackTrace (which basically ends up calling Throwable.printStackTrace() ), all I get is:
Has anyone else come across this? I tried googling for "java null pointer empty stack trace" but didn't come across anything like this.
What is the context? Are there multiple threads involved? I've had issues trying to get the stack trace of an exception in a SwingWorker.
10 Answers 10
You are probably using the HotSpot JVM (originally by Sun Microsystems, later bought by Oracle, part of the OpenJDK), which performs a lot of optimization. To get the stack traces back, you need to pass the option -XX:-OmitStackTraceInFastThrow to the JVM.
The optimization is that when an exception (typically a NullPointerException) occurs for the first time, the full stack trace is printed and the JVM remembers the stack trace (or maybe just the location of the code). When that exception occurs often enough, the stack trace is not printed anymore, both to achieve better performance and not to flood the log with identical stack traces.
To see how this is implemented in the HotSpot JVM, grab a copy of it and search for the global variable OmitStackTraceInFastThrow . Last time I looked at the code (in 2019), it was in the file graphKit.cpp.
Thanks for the tip. Any idea if there are any hidden gotchas to passing this option (it seems pretty innocuous as long as my application doesn't throw a ton of exceptions)?
There are no hidden gotchas that I know of. When you look at the Hotspot source code, you can see that this option is only used in one place (graphKit.cpp). And that looks fine to me.
I am running an OpenJDK JVM, version 1.8.0u171 (Debian 9), and it seems to accept the -XX:-OmitStackTraceInFastThrow flag as well. I've yet to confirm if that was why I was also failing to print stack-traces (e.g., using e.printStackTrace ), but it seems highly likely. I've expanded the answer to reflect this discovery.
In our case first 125 exceptions had a stack trace, and then the rest across 3 rotations of log files had none. This answer was very helpful in finding the culprit.
As you mentioned in a comment, you're using log4j. I discovered (inadvertently) a place where I had written
instead of the typical
through laziness or perhaps just not thinking about it. The unfortunate part of this is that it doesn't behave as you expect. The logger API actually takes Object as the first argument, not a string - and then it calls toString() on the argument. So instead of getting the nice pretty stack trace, it just prints out the toString - which in the case of NPE is pretty useless.
Perhaps this is what you're experiencing?
We actually have a standard policy of never using the first form above (LOG.error(exc);) - we always use the 2 parameter signature so that we add some descriptive statement to the logs instead of just a raw stacktrace.
Sure, but policy doesn't mean it's always executed correctly! Figured it was worth mentioning, at least.
We have seen this same behavior in the past. It turned out that, for some crazy reason, if a NullPointerException occurred at the same place in the code multiple times, after a while using Log.error(String, Throwable) would stop including full stack traces.
Try looking further back in your log. You may find the culprit.
EDIT: this bug sounds relevant, but it was fixed so long ago it's probably not the cause.
The bug is closed, but the -XX:-OmitStackTraceInFastThrow flag is still needed to workaround the performance optimization.
I've been seeing this recently a lot. Any clues as to what might be causing this, or how to fix it? The logging system may have been up for days, and the actual cause rotated out, nevermind the tedious search.
I've tested it on Mac OS X
- java version "1.6.0_26"
- Java(TM) SE Runtime Environment (build 1.6.0_26-b03-383-11A511)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-383, mixed mode)
For this specific fragment of code, 12288 iterations (+frequency?) seems to be the limit where JVM has decided to use preallocated exception.
exception.toString does not give you the StackTrace, it only returns
a short description of this throwable. The result is the concatenation of:
Use exception.printStackTrace instead to output the StackTrace.
Sorry, I misspoke in my original post. I'm logging these through Log4J, which does use printStackTrace().
If you are using log4j, be sure to send the exception as part of the argument to the log method. I will post an answer with that.
@raviaw valid point! @Edward Shtern: can you confirm that you definitely are using the 2-arg form of the log4j method? I know you mentioned in an answer further down that it is the company policy to do so, but are you ABSOLUTELY sure that in this case you are following the policy?
It might be a long shot, but is it possible that the exception originates in some 3rd party code? Maybe it is a (poorly written) exception wrapper, whose toString() simply returns the class name of the wrapped exception, and which fails to provide the underlying stack trace. Try put something like logger.info("Exception + exc.class.getCanonicalName()) into your catch block and see what you get.
Alternate suggestion - if you're using Eclipse, you could set a breakpoint on NullPointerException itself (in the Debug perspective, go to the "Breakpoints" tab and click on the little icon that has a ! in it)
Check both the "caught" and "uncaught" options - now when you trigger the NPE, you'll immediately breakpoint and you can then step through and see how exactly it is handled and why you're not getting a stack trace.
toString() only returns the exception name and the optional message. I would suggest calling
to dump the message, or if you need the gory details:
This will output the Exception, use only to debug you should handle you exceptions better.
(Your question is still unclear on whether your code is calling printStackTrace() or this is being done by a logging handler.)
Here are some possible explanations about what might be happening:
The logger / handler being used has been configured to only output the exception's message string, not a full stack trace.
Your application (or some third-party library) is logging the exception using LOG.error(ex); rather than the 2-argument form of (for example) the log4j Logger method.
The message is coming from somewhere different to where you think it is; e.g. it is actually coming some third-party library method, or some random stuff left over from earlier attempts to debug.
The exception that is being logged has overloaded some methods to obscure the stacktrace. If that is the case, the exception won't be a genuine NullPointerException, but will be some custom subtype of NPE or even some unconnected exception.
I think that the last possible explanation is pretty unlikely, but people do at least contemplate doing this kind of thing to "prevent" reverse engineering. Of course it only really succeeds in making life difficult for honest developers.
Читайте также: