`
cantellow
  • 浏览: 847293 次
  • 性别: Icon_minigender_1
  • 来自: 草帽海贼团
社区版块
存档分类
最新评论

我承认我没有if(xxx != null)

    博客分类:
  • Java
 
阅读更多

 

昨天正在编码兴头时被项目组xx叫去,说我修改的代码出了空指针异常,还说关键的地方比如这是界面的发起部分,就应该判断是否空指针,不然过不去。我看了一下他的环境,说你没有我的数据库脚本,如果有了数据库环境,应该就不会出现这种情况,然后他就说你这个方法本来就有抛出NullPointerException的可能性,为什么调用完之后不判断呢?而且这是程序的关键部分,出了问题界面都发不起。嗯,好吧,我承认这是我的失误,随即加了if(xxx != null)判断。

对于这件事,引发了我对空指针最佳实践的思考,想必我们使用最多的方法就是在可能发生空指针异常的地方加上:if(xxx != null),每个人都对抛出空指针异常的可能性都不同,换句话说就是这种可能性很难统一和界定,如果使用这种方式,那么代码肯定奇丑难看,到处都是if(xxx != null)else…。而且else部分也不一样,要么什么都不做,要么直接返回提示错误,要么就重新赋一个默认的值。

还有种方式就是在产生return null的地方用new对象代替,这个new对象纯粹是为了防止客户类出现null,可是这样做的可读性降低了,个人认为跟脱了裤子放屁没两样。Apireturn 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代替。

分享到:
评论
28 楼 提烟而过 2010-09-08  
个人认为:如果无异常处理机制的系统里,在后台程序里只要有对对像进行操作,都应该做NULL值判定。程序的严谨性远远重于程序的可读性。如果页面有用到JAVA代码而不是用的标签,更应该考虑到这一点。否则用户一点一大堆看不懂的代码就出来了,你做的系统会大大打折的!
27 楼 peterwei 2010-09-08  
elmar 写道
判断是不是NULL是caller的责任,而不是callee的责任。Callee可以假设传入的值是合法的。

我一般在某些场景:callee也判判一下。
假设一个场景:批量删除操作
jsp page:
一堆checkbox,id都是:objectIds

action层:
String[] warnIds;

delete(String[] warnIds){
   service.remove(warnIds);
}

Service层

remove(String[] warnIds){
		if (warnIds != null){

			for (int i = 0; i < warnIds.length; i++) {
				Warn warnTemp = (Warn) warnDao.getObject(
						Warn.class, Long.parseLong(warnIds[i]));
				if (warnTemp != null) {
					//Warn warn = new Warn();
					// BeanUtils.copyProperties(warn, warnTemp);
					warnTemp.setDealIndex(new Long(2));
					warnDao.updateObject(warnTemp);
//					warn.setMsgId(warnTemp.getMsgId());
//					warn.setWarnLevel(warnTemp.getWarnLevel());
//					warn.setWarnMsg(warnTemp.getWarnMsg());
//					warnDao.saveObject(warn);
//					warnDao.removeObject(warnTemp);
				}
			}
		}
}
26 楼 cantellow 2010-09-08  
鱼言风语 写道
cantellow 写道
鱼言风语 写道
UI的validation应该判断,其它地方应该不判断,让exception抛出来吧?


好像是你们team leader不对

难道后台就不会产生空指针?



有2种情况,第一种是不应该为空的,比如你首贴的那个场景,如果是契约式编程,不应为空,那你不需要判断。出这种异常要做的事情就是检查环境配置。否则,虽然可以用assertNotNull,但还是很烦的。

第二种情况是有可能为空,比如表的某个字段,这种当然需要判断

比较同意,总结一点就是,某些情况下空指针的触发是因为其他的错误根源导致,那么就应该抛出,检查环境配置,修正问题的根源。某些情况下null的来源是“很正常的”,比如用户输入的情况,数据库的字段等等,那么这时不得不判断,或者使用断言。
25 楼 cantellow 2010-09-08  
struts 写道
页面用el表达式.

我是做swing的
24 楼 peterwei 2010-09-08  
fhyfufangyu 写道
JSP+EL :NullPointer的时候直接输出null,让我想起了卓越亚马逊的包裹地址:XX市XX区XX街null号。。。。
Freemarker:矫枉过正,到处都是XXX?if_exists  或者xx?default('')

以前做freemarker时,页面判断确实很郁闷,很多判断。List?XX.object?XX.subObject?XX.property?XX
23 楼 cantellow 2010-09-08  
luciferdevil 写道
一般需要在给页面对象或者List时,我会常常在获取对象的那个方法有个new:
List<String> list=new ArrayList();
这样无论这个方法最终有没有得到数据,这个list都不会是null的,页面判断的时候只是if(list.size()==0){//对不起没有找到相关的数据}
用null的话 一个不好 就给用户500错误(以前在项目测试前期会出现这个……)


这个还是要看情况,或许在你这种情况下这种处理方式比较好。
但是换一种情况,想象一个复合数据结构,它的值跟用户没有任何关系,完全是后台业务逻辑所致。
如果本身因为某些不正当操作,比如解析一个XML文件,可能是因为你的XML文件格式有关,多写了一个<或者多写了一个/>,那么解析出错很可能返回的是null,如果你new一个对象,客户类怎么知道你这个对象是正常逻辑得到的?
客户类肯定会因为这个对象的数据时正常的,所以xml格式的错误一直不会发现,那么这可能会很悲剧。

个人认为如果对象跟用户的操作有关(比如输入),那么就应该判断,或者在用户操作的时候就做一些限制,而如果对象的生成跟业务逻辑是否正确有关,那么空指针就应该大胆的抛出,并做好log,为了好解决问题的根源所在。
22 楼 鱼言风语 2010-09-08  
cantellow 写道
鱼言风语 写道
UI的validation应该判断,其它地方应该不判断,让exception抛出来吧?


好像是你们team leader不对

难道后台就不会产生空指针?



有2种情况,第一种是不应该为空的,比如你首贴的那个场景,如果是契约式编程,不应为空,那你不需要判断。出这种异常要做的事情就是检查环境配置。否则,虽然可以用assertNotNull,但还是很烦的。

第二种情况是有可能为空,比如表的某个字段,这种当然需要判断
21 楼 cantellow 2010-09-08  
鱼言风语 写道
UI的validation应该判断,其它地方应该不判断,让exception抛出来吧?


好像是你们team leader不对

难道后台就不会产生空指针?
20 楼 cantellow 2010-09-08  
mathfox 写道
不能绝对化,不过,你的确是不应该判断

判断也没有什么用,也没有解决方案。还不如早把问题暴露出来。


可是在程序的关键部分,你也这么做么?我认为,在有些时候,判断一下还是很有用的。
19 楼 elmar 2010-09-08  
判断是不是NULL是caller的责任,而不是callee的责任。Callee可以假设传入的值是合法的。
18 楼 cantellow 2010-09-08  
paranoid945 写道
责任分明
比如collections.sort方法就不判断null,因为它觉得判断null不是他的责任

嗯,比如说契约设计。
17 楼 xly_971223 2010-09-08  
相对独立的模块或者提供给第三方调用的模块需要做null判断
项目内部的代码就不用那么严格了 会死人的
16 楼 lkj107 2010-09-08  
异常总体捕捉一次,给客户一个专门的错误界面,例如请联系***
15 楼 fhyfufangyu 2010-09-08  
JSP+EL :NullPointer的时候直接输出null,让我想起了卓越亚马逊的包裹地址:XX市XX区XX街null号。。。。
Freemarker:矫枉过正,到处都是XXX?if_exists  或者xx?default('')
14 楼 lzmhehe 2010-09-08  
建议你使用短语机制
看看Spring的Assert
在程序的入口处多参数进行断言
13 楼 zlm_525 2010-09-08  
不做判断只有死路一条。
优秀的程序员是即使过单行道的时候都会向道路两边看看再过去。这话这几天炒得很火。
12 楼 httpclient_bd 2010-09-08  
你们声明接口API的时候不通过DOC提示调用者可能返回Null以及在什么情况下返回NULL吗?
11 楼 Rooock 2010-09-08  
当然应该根据上下文的业务需求, 捕获处理, 或者重新抛出异常.

无论如何. 只能给用户界面显示友好的错误消息.
10 楼 mallon 2010-09-08  
宽进严出,很正常的,或者偷懒的话在方法上throws Exception,在某一个地方统一处理就是了
9 楼 struts 2010-09-08  
页面用el表达式.

相关推荐

    基于Qt的读取配置文件

    = NULL){ strRet = m_iniFile-&gt;GetValue("moonlightService", "DIPAddr"); } 4.最后在析构函数记得再判断释放这个碎片化的堆空间(严谨做法) 举例: if (m_iniFile != NULL){ delete m_iniFile; m_...

    图片文件上次,获取图片文件实际类型

    if (null != bis) { bis.close(); } if (null != fis) { fis.close(); } } catch (IOException e) { e.printStackTrace(); } } return true; } /** * ...

    可以读取EXCEL文件的js代码宣贯.pdf

    = null || excelWorkBook != undefined) { excelWorkBook.Close(); } if (excelApp != null || excelApp != undefined) { excelApp.Application.Quit(); excelApp = null; } } } ``` 这段代码首先创建了一个...

    多种方法使用java空指针功能.docx

    =`运算符可以安全地用于比较`null`,但`&gt;`、`等运算符会因为无法对`null`进行计算而抛出异常。 #### 4. `xxx == null`与`null == xxx`的等价性 在Java中,`xxx == null`与`null == xxx`是等价的,并且无论哪种写法...

    在View中显示“您好,我是XXX文本在View内自左向右循环滚动

    1.在View中显示“您好,我是XXX!”。 2.文本在View内自左向右循环滚动; 3.文字在红、蓝、绿、黄四种颜色之间周期变化。红(255,0,0)蓝(0,255,0)绿(0, 0,255)黄(255,255,0);

    循环列队数据结构课题舞会配对的问题

    = NULL) && (bSrc != NULL)); char* address = aDest; while ((*aDest++ = *bSrc++) != '\0') NULL; return address; } ``` **说明:** 该函数用于将一个字符串复制到另一个字符串中。 #### 2. 字符串比较 `...

    关于System.Convert的那些事儿

    =null && Convert.ToBoolean(obj)==true){ //Do something}上面的代码的意图很明显,先判断obj 是不是null,如果不是null 的话,再调用Convert.ToBoolean方法,判断是不是True。 上面的方法没有任何问题,但有一点...

    c#和net存取cookies操作示例

    = null && Request.Cookies[strName].Values[subKey] != null) return Server.HtmlEncode(Request.Cookies[strName][subKey].Value); else return null; } } ``` 使用这个类,你可以在应用程序中方便地调用`...

    vc++游戏编程指南

    3. **错误检测**:在整个初始化过程中,频繁使用`if(xxx != DD_OK)`的方式进行错误检测,确保每一步操作都正确无误。 ### 结论 通过以上内容,我们可以了解到DirectDraw在游戏开发中的重要性和基本使用流程。...

    org.apache.tools.zip解决解压乱码问题

    if (null != zos) { zos.close(); } } } /** * * 功能描述:压缩文件 * 创建者:XXX * 创建日期: 2015年5月7日 - 下午1:37:55 * 版本: V0.1 * 修改者: * 修改日期: ...

    前端大厂最新面试题-typeof_instanceof.docx

    == 'object' || left === null) return false; // getPrototypeOf 是 Object 对象自带的 API,能够拿到参数的原型对象 let proto = Object.getPrototypeOf(left); while (true) { if (proto === null) return ...

    JFileChooser使用详解.pdf

    = null && extension.equalsIgnoreCase(".png")) { return true; } } return false; } public String getDescription() { return "PNG图片"; } private String getExtension(File f) { if (f != null) { ...

    velocity为空判断

    #ifnotnull($XXX) 变量$XXX不为null #else 变量$XXX为null #endif ``` #### 方法二:`$null.isNull()` 这是一个更直接的检查方式,无需额外配置。推荐使用此方法,特别是当判断集合对象是否为null时,非常实用。...

    grpc最简单的demo案例

    if err != nil { log.Fatalf("failed to listen: %v", err) } log.Println("Server is running on port 50051...") if err := server.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } ``` 5....

    VC++ 折线图 柱状去 条形图

    我自己的VC++课程设计作业。 int k=0; srand((unsigned)time(NULL));... if(j%3!=0) { ofs(4)☆★☆★☆"; } else //三个数换行 数字后面不带符号 { printf("\n"); ofs(4); } } ofs.close();

    限制pc端访问页面的代码

    == "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1); } if (!isMobileDevice()) { alert("对不起,此页面仅支持移动设备访问。"); // 或者重定向到其他页面 window.location.href = ...

    程序问候,Hello,xxx!

    输入名字,程序就会自动问候你,是不是很神奇啊!? 那就快一点下来看看吧!!体验不一样的感觉吧!!

    Mybatis单个参数的if判断报异常There is no getter for property named 'xxx' in 'class java.lang.Integer'的解决方案

    = null and langId != null"&gt; and lang_id = #{langId} &lt;/if&gt; ``` 在这个修正后的代码中,`test`属性的条件表达式检查了`_parameter`对象是否存在(即参数不为空)以及`langId`是否不为空。当`langId`有值时,...

    ResultSet

    = null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } ``` 6. 结果集的状态:ResultSet有三种状态:未初始化、已移动到下一行和结束。调用next()会改变其状态,且一旦到达...

    高精度类(无压位),C++,基本可以像基本类型一样使用

    =,&gt;,&lt;,&gt;=,&lt;=:比较大小,第二个操作数(运算符后面那个)支持IAKIOI,int 时间复杂度,n表示第一个操作数,m表示第二个操作数: 加法:O(max(n, m)) 减法:O(max(n, m)) 乘法:O(nm),抱歉我不会 FFT。 除法:O...

Global site tag (gtag.js) - Google Analytics