Вопрос с 250 Вт просмотров страниц на Stack Overflow: вы потеряли свой объект

Java

Когда я просматривал Stack Overflow, я обнаружил, что самым популярным вопросом оказался: Что такое NullPointerException (java.lang.NullPointerException), что вызвало это, и есть ли хороший способ или инструмент, чтобы отследить, почему это произошло?

Неожиданно этот вопрос просмотрели 2,5 миллиона раз! Итак, я подумал, что пришло время разобраться и поделиться наиболее популярными ответами. Пожалуйста, пойдем со мной.

Когда вы объявляете ссылочную переменную (то есть объект), вы фактически создаете указатель на объект. См. следующий код:

int x;
x = 10;

Первая строка кода объявляет переменную с именем x (типа int), которую Java инициализирует значением 0. Вторая строка кода присваивает x значение 10, что означает, что 10 будет записано в ячейку памяти, на которую указывает x.

Однако когда мы попытаемся объявить ссылочный тип, ситуация будет иной.

Integer num;
num = new Integer(10);

Первая строка кода объявляет переменную с именем num (типа Integer), которую Java инициализирует нулевым значением, что означает «ни на что не указывает».

Во второй строке кода ключевое слово new создает объект типа Integer и указывает переменную num на этот объект.

NullPointerException возникает, когда мы объявляем переменную, не указывая переменную на какой-либо созданный объект, а затем используем ее. В большинстве случаев компилятор обнаружит эту проблему и предупредит нас, что "xxxx, возможно, не был инициализирован".

Если есть такой кусок кода:

public void doSomething(SomeObject obj) {
   //do something to obj
}

В этом случае вместо создания объекта obj мы предполагаем, что он находится вdoSomething()Создается до вызова метода.

Теперь предположим, что он не был создан до этого. мы так звонимdoSomething()метод:

doSomething(null);

это означаетdoSomething()Параметр метода obj имеет значение null. Если метод собирается продолжать что-то делать с obj, лучше бросить раньшеNullPointerException, так как эта информация нужна разработчикам для отладки.

Существует еще одна альтернатива, чтобы определить, является ли obj нулевым, и если да, будьте осторожны и сделайте что-нибудь, что не вызовет исключение NullPointerException; если нет, просто сделайте то, что оно делает.

/**
  * @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
    }
}

Итак, если программа действительно имеет NullPointerException, как отследить информацию о стеке и найти источник ошибки?

Проще говоря, информация о стеке — это список методов, вызываемых приложением при возникновении исключения, которые могут указать источник ошибки. Как показано ниже.

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Что касается приведенной выше информации о стеке, ошибка возникает в списке "at...", и первое "at" — это место, где изначально возникла ошибка.

at com.example.myproject.Book.getTitle(Book.java:16)

Для отладки мы можем открыть строку 16 класса Book.java, которая может быть:

15   public String getTitle() {
16      System.out.println(title.toString());
17      return title;
18   }

Как видно из этого кода, причина ошибки, скорее всего, в том, что title имеет значение null.

Иногда приложение перехватывает исключение и создает его как исключение другого типа. Как следующее:

34   public void getBookIds(int id) {
35      try {
36         book.getId(id);    // 这里可能会引发 NullPointerException
37      } catch (NullPointerException e) {
38         throw new IllegalStateException("A book has a null property", e)
39      }
40   }

Информация о стеке в это время может быть следующей:

Exception in thread "main" java.lang.IllegalStateException: A book has a null property
        at com.example.myproject.Author.getBookIds(Author.java:38)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Caused by: java.lang.NullPointerException
        at com.example.myproject.Book.getId(Book.java:22)
        at com.example.myproject.Author.getBookIds(Author.java:36)
        ... 1 more

Отличие от предыдущей информации в стеке состоит в том, что «Вызвано» на одно больше, «Вызвано» иногда больше. В этом случае нам обычно нужно вернуться к источнику и найти «причину» на самом глубоком уровне — это тот, который находится внизу стека информации.

Caused by: java.lang.NullPointerException <-- 根本原因
        at com.example.myproject.Book.getId(Book.java:22) 

Опять же, нам нужно посмотреть на строку 22 Book.java, чтобы найтиNullPointerExceptionпричина.

Иногда информация о стеке гораздо более беспорядочна, чем в приведенном выше примере. См. этот ниже.

javax.servlet.ServletException: Something bad happened
    at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:60)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.example.myproject.ExceptionHandlerFilter.doFilter(ExceptionHandlerFilter.java:28)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.example.myproject.OutputBufferFilter.doFilter(OutputBufferFilter.java:33)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: com.example.myproject.MyProjectServletException
    at com.example.myproject.MyServlet.doPost(MyServlet.java:169)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2822)
    at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321)
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:344)
    at $Proxy19.save(Unknown Source)
    at com.example.myproject.MyEntityService.save(MyEntityService.java:59) <-- relevant call (see notes below)
    at com.example.myproject.MyServlet.doPost(MyServlet.java:164)
    ... 32 more
Caused by: java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
    at org.hsqldb.jdbc.Util.throwError(Unknown Source)
    at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:57)
    ... 54 more

Количество информации о стеке в этом примере просто ошеломляет. Если вы будете следовать методу, предоставленному ранее (нижний в информации стека), чтобы найти самую глубокую «причину», это:

Caused by: java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
    at org.hsqldb.jdbc.Util.throwError(Unknown Source)
    at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
    at org.hibernate.id.insert.AbstractSelec

Но на самом деле это не так, потому что вызывающий метод, выбрасывающий это исключение, относится к коду библиотеки классов (библиотека классов c3p0), поэтому нам нужно найти причину исключения, и это исключение, скорее всего, будет кодом, написанным сами (com.example.myprojectpackage), поэтому мы нашли такой кусок информации об исключении.

at com.example.myproject.MyEntityService.save(MyEntityService.java:59)

Просто следите за потоком и посмотрите на строку 59 MyEntityService.java, которая является основной причиной ошибки.

Спасибо, что читаете. Оригинальность — это непросто. Если вам это нравится, поставьте лайк. Это будет моей самой сильной мотивацией к написанию. Если вы считаете, что статья полезна и интересна вам, обратите внимание на мой паблик, спасибо.

PS: Позвольте мне сказать вам по секрету, что вы также можете получить расширенные данные Java на сумму 399 юаней, ответив на «Java» в фоновом режиме, тсс.