Stack Overflow 上 250W 浏览量的一个问题:你对象丢了
在逛 Stack Overflow 的时候,发现最火的问题竟然是:什么是 NullPointerException(java.lang.NullPointerException),它是由什么原因导致的,有没有好的方法或者工具可以追踪它发生的原因?
真没想到,这个问题浏览的次数多达 250 万次!所以,我想是时候把最高赞的回答整理一下分享出来了。请随我来。
声明引用变量(即对象)时,实际上是创建了一个指向对象的指针。请看以下代码:
int x;x = 10;
第一行代码声明了一个名为 x 的变量(int 类型),Java 会把它初始化为 0。第二行代码把 x 赋值为 10,意味着 10 将被写入到 x 所指向的内存位置上。
但是呢,当我们尝试声明一个引用类型时,情况将会有所不同。
Integer num;num = new Integer(10);
第一行代码声明了一个名为 num 的变量(Integer 类型),Java 把它初始化为 null,表示“什么都没有指向 ”。
第二行代码中,new 关键字创建了一个 Integer 类型的对象,并将变量 num 指向该对象。
当我们声明了一个变量,却没有将该变量指向任何创建的对象,然后就使用它的时候,NullPointerException 就发生了。大多数情况下,编译器会发现这个问题,并且提醒我们“xxxx may not have been initialized”。
假如有这样一段代码:
public void doSomething(SomeObject obj) { //do something to obj}
在这种情况下,我们没有创建对象 obj,而是假设它在 doSomething() 方法被调用之前就创建了。
现在假设在此之前它没有创建。我们这样调用 doSomething() 方法:
doSomething(null);
这就意味着 doSomething() 方法的参数 obj 为 null。如果该方法还要使用 obj 继续做点什么,最好提前抛出 NullPointerException,因为开发者需要该信息来进行调试。
还有另外一种替代方法,判断 obj 是不是 null,如果是,就小心行事,做某些不会引起 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 时调用的方法列表,可以准确地定位到错误发生的根源。就像下面这样。
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)
为了调试,我们可以打开 Book.java 类的第 16 行,它可能是:
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); // 这里可能会引发 NullPointerException37 } 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”;有时候还会有更多的“Caused by”。在这种情况下,我们通常需要追本溯源,找到最深层次的那个“cause”——它就是堆栈信息中最下面的那个。
Caused by: java.lang.NullPointerException <-- 根本原因 at com.example.myproject.Book.getId(Book.java:22)
同样,我们需要查看一下 Book.java 的第 22 行,找到可能引发 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 moreCaused 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
这个例子当中的堆栈信息实在是太多了,令人眼花缭乱。如果按照之前提供的方法(堆栈信息中最下面的那个)找最深层次的那个“cause”,它就是:
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.myproject 包下)引发的,于是我们找到了这样一段异常信息。
at com.example.myproject.MyEntityService.save(MyEntityService.java:59)
顺藤摸瓜,看看 MyEntityService.java 的第 59 行,它就是引发错误的根本原因。
谢谢大家的阅读,原创不易,喜欢就点个赞,这将是我最强的写作动力。如果你觉得文章对你有所帮助,也蛮有趣的,就关注一下我的公众号,谢谢。
Stack Overflow 上 250W 浏览量的一个问题:你对象丢了的更多相关文章
- Stack Overflow 上 370万浏览量的一个问题:如何比较 Java 的字符串?
在逛 Stack Overflow 的时候,发现了一些访问量像喜马拉雅山一样高的问题,比如说这个:如何比较 Java 的字符串?访问量足足有 370万+,这不得了啊!说明有很多很多的程序员被这个问题困 ...
- [转帖]Stack Overflow上188万浏览量的提问:Java 到底是值传递还是引用传递?
Stack Overflow上188万浏览量的提问:Java 到底是值传递还是引用传递? http://www.itpub.net/2019/12/03/4567/ 在逛 Stack Overfl ...
- 为什么开发者热衷在Stack Overflow上查阅API文档?
摘要:一项新研究跟踪了Android开发者的访问历史,发现开发者多达二分之一的文档是从Stack Overflow上获取到的,而Stack Overflow上的示例也多于官方指南,开发者通过搜索更多时 ...
- Stack Overflow 上排名前十的与API相关的问题
Stack Overflow是一个庞大的编程知识仓库,在Stack Overflow 上,数百万的提问被回答,并且这些回答都是高质量的.这就是为什么在Google搜索结果的排行榜上,Stack Ove ...
- Stack Overflow上关于Java Collections的几个常见问题
下面列出Stack Overflow上最常见的几个关于Java Collections的问题并给出答案. 1. 什么时候用LinkedList,什么时候用ArrayList? ArrayList是使用 ...
- JavaScript超越了Java,c,python等等成为Stack Overflow上最热门的
JavaScript超越了Java,c,python等等成为Stack Overflow上最热门的标签 在2015年6月至今,JavaScript超越了Java,c,python等等成为Stack O ...
- Stack Overflow上59万浏览量的提问:为什么会发生ArrayIndexOutOfBoundsException?
在逛 Stack Overflow 的时候,发现了一些访问量像昆仑山一样高的问题,比如说这个:为什么会发生 ArrayIndexOutOfBoundsException?这样看似简单到不值得一问的问题 ...
- 关于Stack Overflow上ASP.NET最大连接数限制提问的一个思考
原文地址:Why request queuing is high even when request executing is below its limit? We are using below ...
- Stack Overflow 上人气最旺的 10 个 Java 问题
1. 为什么两个(1927年)时间相减得到一个奇怪的结果? (3623个赞) 如果执行下面的程序,程序解析两个间隔1秒的日期字符串并比较: public static void main(String ...
随机推荐
- http压力测试工具及使用说明
http压力测试工具及使用说明 转 说明:介绍几款简单.易使用http压测工具,便于研发同学,压测服务,明确服务临界值,寻找服务瓶颈点. 压测时候可重点以下指标,关注并发用户数.TPS(每秒事务数量) ...
- Git使用教程 Windows使用Git全攻略
在Windows上安装Git 我们需要先把Git的安装包下载下来,访问网址:https://git-for-windows.github.io/ 或者百度网盘:链接: https://pan.baid ...
- SpringDataJpa——JpaRepository查询功能(转)
1.JpaRepository支持接口规范方法名查询.意思是如果在接口中定义的查询方法符合它的命名规则,就可以不用写实现,目前支持的关键字如下. Keyword Sample JPQL snippet ...
- mgo操作mongodb
mgo基本使用: http://labix.org/mgo 安装 # go get gopkg.in/mgo.v2 package main import ( "fmt" &quo ...
- (六十九)c#Winform自定义控件-垂直滚动条
前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...
- 关于W3Cschool定义的设计模式-常用的9种设计模式的介绍
一.设计模式 tip:每种设计模式,其实都是为了更高效的,更方便的解决在面对对象编程中所遇到的问题. 什么是设计模式: 是一套经过反复使用.多人知晓的.经过分类的.代码设计经验的总结 为什 ...
- NestedInteger Java
''' class NestedInteger { private List list; private Integer integer; public NestedInteger(List<N ...
- 转载:elastic5.x部署常见问题总结
原博文名称:ElasticSearch 5.0.0 安装部署常见错误或问题 原博文地址为:http://www.dajiangtai.com/community/18136.do?origin=csd ...
- java.sql.SQLException: Data truncation: Incorrect string value: '\xE5\x91\xA8\xE6\x9D\xBE' for column 'cname' at row 1 Query
在将项目上传到服务器时,发生这样的错误,总结了一下. 环境Centos7+tomcat7+Mariadb数据库 首先mysql -uroot -p 输入密码 然后查询数据库当前码表状态 show va ...
- Redis高级客户端Lettuce详解
前提 Lettuce是一个Redis的Java驱动包,初识她的时候是使用RedisTemplate的时候遇到点问题Debug到底层的一些源码,发现spring-data-redis的驱动包在某个版本之 ...