A problem with 250W views on Stack Overflow: your object is lost

Keywords: Java Jetty Hibernate JDBC

When browsing Stack Overflow, I found that the most popular problem is: what is NullPointerException (java.lang.NullPointerException), what causes it, and whether there is a good way or tool to track the cause of it?

I didn't expect to see as many as 2.5 million views of this problem! So, I think it's time to sort out the best answers and share them. Please follow me.

When you declare a reference variable (that is, an object), you actually create a pointer to the object. See the following code:

int x;
x = 10;

The first line of code declares a variable (int type) named x, which Java initializes to 0. The second line assigns x a value of 10, which means that 10 will be written to the memory location x points to.

However, when we try to declare a reference type, the situation will be different.

Integer num;
num = new Integer(10);

The first line of code declares a variable (Integer type) named num, which Java initializes as null, meaning "nothing points."

In the second line of code, the new keyword creates an object of type Integer and points the variable num to it.

NullPointerException occurs when we declare a variable without pointing it to any created object and then use it. In most cases, the compiler will find this problem and remind us "xxxx may not have been initialized".

If there is such a code:

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

In this case, instead of creating the object obj, we assume that it was created before the doSomething() method was called.

Now suppose it wasn't created before. We call the doSomething() method as follows:

doSomething(null);

This means that the parameter obj of the doSomething() method is null. If the method still needs to use obj to do something, it is better to throw NullPointerException in advance, because the developer needs this information for debugging.

There is another alternative method to judge whether obj is null. If it is, be careful and do something that will not cause NullPointerException. If not, be sure to do something boldly.

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

If NullPointerException does appear in the program, how to trace the stack information and find the source of the error?

Simply put, the stack information is the list of methods that an application calls when it raises an Exception, and can accurately locate the source of the error. It's like this.

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)

In terms of the above stack information, the error occurs in the "at..." list, and the first "at" is where the error first occurred.

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

To debug, we can open line 16 of the Book.java class, which may be:

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

As you can see from this code, the most likely cause of the error is that the title is null.

Sometimes, an application catches an exception and throws it as another type of exception. As follows:

34   public void getBookIds(int id) {
35      try {
36         book.getId(id);    // NullPointerException may be raised here
37      } catch (NullPointerException e) {
38         throw new IllegalStateException("A book has a null property", e)
39      }
40   }

The stack information at this time may be as follows:

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

Different from the previous stack information, there is an additional "Caused by"; sometimes there will be more "Caused by". In this case, we usually need to trace the origin and find the deepest "cause" - the lowest one in the stack information.

Caused by: java.lang.NullPointerException <-- Root cause
        at com.example.myproject.Book.getId(Book.java:22) 

Again, we need to look at line 22 of Book.java to find out why NullPointerException might be raised.

Sometimes, the stack information is much more cluttered than in the example above. Refer to this one below.

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

The stack information in this example is so much that it is dazzling. If you find the deepest "cause" according to the previously provided method (the lowest one in the stack information), it is:

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

But in fact, it's not. Because the method caller who threw the exception belongs to the class library code (c3p0 Class Library), we need to look up the cause of the exception, and the exception is probably caused by our own code (com.example.myproject package), so we found such an exception information.

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

Take a look at line 59 of MyEntityService.java, which is the root cause of the error.

Thank you for your reading. It's not easy to be original. Just like it. It will be my strongest writing motivation. If you think the article is helpful and interesting for you, please pay attention to my public account. Thank you.

Posted by maybl8r03 on Sun, 20 Oct 2019 19:43:41 -0700