昨天正在编码兴头时被项目组xx叫去,说我修改的代码出了空指针异常,还说关键的地方比如这是界面的发起部分,就应该判断是否空指针,不然过不去。我看了一下他的环境,说你没有我的数据库脚本,如果有了数据库环境,应该就不会出现这种情况,然后他就说你这个方法本来就有抛出NullPointerException的可能性,为什么调用完之后不判断呢?而且这是程序的关键部分,出了问题界面都发不起。嗯,好吧,我承认这是我的失误,随即加了if(xxx != null)判断。
对于这件事,引发了我对空指针最佳实践的思考,想必我们使用最多的方法就是在可能发生空指针异常的地方加上:if(xxx != null),每个人都对抛出空指针异常的可能性都不同,换句话说就是这种可能性很难统一和界定,如果使用这种方式,那么代码肯定奇丑难看,到处都是if(xxx != null)else…。而且else部分也不一样,要么什么都不做,要么直接返回提示错误,要么就重新赋一个默认的值。
还有种方式就是在产生return null的地方用new对象代替,这个new对象纯粹是为了防止客户类出现null,可是这样做的可读性降低了,个人认为跟脱了裤子放屁没两样。Api中return null这种写法也比较常见,比较符合OO逻辑。
既然不能避免null,那么你就必须小心翼翼,因为你对每个对象的操作之前你都不能保证它一定是非null,除非你做了判断,Api中为了保证某些值一定不能为null时,采用这种方式:if (name == null) {throw new NullPointerException();}。那我们自己的程序能不能这样写呢?我觉得大多数java程序的现状就是,关键地方使用了if(xxx != null)判断,某些边缘没有判断,所以经常会发生空指针异常。
好像findBugs可以找到可能发生null的地方,不知道它的建议做法是什么?面对NullPointerException,各位有没有更好的做法呢?
====================================================================================
被投隐了,不过没关系,关于几点我还要说明一下,NullPointerException是一个runtimeException,它不会像车checkedException,要么让你“吞掉”,要么让你抛出,就因为它是uncheckedException,所以我们无法精确判断哪个地方会抛出NullPointerException(其实大多数时候是我们根本没有考虑)。
设计模式中有个null object模式,它的大概意思就是对领域对象继承一个null object,它里面什么都不干,代表一个无的概念,用return null object代替return null,这样就避免了NullPointerException的出现。但是个人建议还是要分场合使用,如果return null代表“这段程序不是出现异常了才返回null,而是null本身就代表了一个无的概念”,再现实一点,遍历一个person集合,查找某个person,如果没有查找到,通常我们返回null,这里,就可以用用return null object代替return null了。
是该总结一下空指针的最佳实践了,还是从空指针产生的根源来说:
1.在应用前端,如果null的产生是一个正常的业务逻辑的分支,比如用户的输入,你并没有限制用户可以什么都不干,那么我们就应该使用if(xxx != null)判断,如果为null,我们会给用户一个提示一个对话框,而不是一大堆异常。
2.如果null的产生是应用真正走向了异常边缘,比如环境配置问题,其他前调方法的错误,那么这时就应该大胆的抛出异常,在方法的前置条件使用断言,或者统一判断可能为null的地方,如果为null就抛出NullPointerException,或者什么都不干,大胆的往前走。举一个我实际遇到的例子,方法的入口是一个复合数据结构,但这个复合数据结构是解析xml得来的,有一天,我写的xml格式不对,导致复合数据结构某些引用指向null,那么这个时候,我们就应该大胆的抛出这个异常,让我们早点发现错误的根源,而不是使用if(xxx != null)判断或者使用一个null object导致我们以为什么问题都没有发生,或者感到不对了,却找了半天的问题根源。
3.在合适的情况下,使用null object,就像前面说的,我们在返回return null的时候用return null object代替。