Stack Overflow 上最火的一个问题:什么是 NullPointerException

加个“星标”,天天中午 12:14,一起快乐成长

在逛 Stack Overflow 的时候,发现最火的问题竟然是:什么是 NullPointerException(),它是由什么原因导致的,有没有好的方法或者工具可以追踪它发生的原因?

真没想到,这个问题浏览的次数多达 250 万次!所以,我想是时候把最高赞的回答整理一下分享出来了。请随我来。

声明引用变量(即对象)时,实际上是创建了一个指向对象的指针。请看以下代码:

第一行代码声明了一个名为 x 的变量(int 类型),Java 会把它初始化为 0。第二行代码把 x 赋值为 10,意味着 10 将被写入到 x 所指向的内存位置上。

但是呢,当我们尝试声明一个引用类型时,情况将会有所不同。

第一行代码声明了一个名为 num 的变量(Integer 类型),Java 把它初始化为 null,表示“什么都没有指向 ”。

第二行代码中,new 关键字创建了一个 Integer 类型的对象,并将变量 num 指向该对象。

当我们声明了一个变量,却没有将该变量指向任何创建的对象,然后就使用它的时候,NullPointerException 就发生了。大多数情况下,编译器会发现这个问题,并且提醒我们“xxxx may not have been initialized”。

假如有这样一段代码:

在这种情况下,我们没有创建对象 obj,而是假设它在 方法被调用之前就创建了。

现在假设在此之前它没有创建。我们这样调用 方法:

这就意味着 方法的参数 obj 为 null。如果该方法还要使用 obj 继续做点什么,最好提前抛出 ,因为开发者需要该信息来进行调试。

还有另外一种替代方法,判断 obj 是不是 null,如果是,就小心行事,做某些不会引起 NullPointerException 的事情;如果不是,就放心大胆地做该做的事情。

那假如程序真的出现了 NullPointerException,该怎么追踪堆栈信息,找到错误的根源呢?

简单来说,堆栈信息是应用程序在引发 Exception 时调用的方法列表,可以准确地定位到错误发生的根源。就像下面这样。

就上面这个堆栈信息来说,错误发生在“at …”列表处,第一个“at 处”就是错误最初发生的位置。

为了调试,我们可以打开 Book.java 类的第 16 行,它可能是:

从这段代码中可以看得出,错误的原因很可能是因为 title 为 null。

有时候,应用程序会捕获一个异常,然后把它作为另外一种类型的异常抛出。就像下面这样:

此时的堆栈信息可能是下面这样的:

和之前堆栈信息有所不同的是,这里多了一个“Caused by”;有时候还会有更多的“Caused by”。在这种情况下,我们通常需要追本溯源,找到最深层次的那个“cause”——它就是堆栈信息中最下面的那个。

同样,我们需要查看一下 Book.java 的第 22 行,找到可能引发 的原因。

有时候,堆栈信息要比上面的例子凌乱得多。参考下面这个。

这个例子当中的堆栈信息实在是太多了,令人眼花缭乱。如果按照之前提供的方法(堆栈信息中最下面的那个)找最深层次的那个“cause”,它就是:

但其实它并不是的,因为抛出这个异常的方法调用者属于类库代码(c3p0 类库),所以我们需要往上找异常发生的原因,并且这个异常很可能是由我们自己编写的代码( 包下)引发的,于是我们找到了这样一段异常信息。

顺藤摸瓜,看看 MyEntityService.java 的第 59 行,它就是引发错误的根本原因。

PS:谢谢大家的阅读,喜欢就点个在看 or 转发朋友圈,这将是我最强的写作动力。昨天恰个饭,结果取消关注的有点多,这说明什么呢?没有取消的都是真爱——被各位宠爱的感觉真好,再次说一声谢谢。

沉默王二

一个真特么有趣的程序员

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20191019A04PP500?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券