虽然现在只是初入职场的IT小菜鸟,但是希望记录下自己工作中的点滴想法,分享给同在挣扎的各位菜鸟,也希望抛砖引玉,得到高手的指正。
阅读对象:对平台自定义异常规划中存在疑问的朋友,也包括百度Google上无意中搜到了各位朋友,谢谢支持!
[零] 一无所获
不知大家是否遇到过这样的问题:当然你写一个DAO层或Service层的查询业务类时,如果查询到的结果为空,你会怎么做?
public UserVO queryUser(String userAccount){
// do query...
if(user==null) {
// 1) throw new NoFoundUserException(); ?
// 2) do nothing
}
return user;
}
你是选择以抛出一个自定义的异常NoFindUserException呢,还是直接返回一个null ?
[壹] null VS NoFoundException
那我们来先比较一下这两者的区别:
null: 大名鼎鼎的null值,表示引用数据类型并不指向任何值,给大家编码中最直观的印象就是NullPointException 与 if( XXX == null) 这种判断方式。
XXXNoFoundExPception: 这里所指的是你自定义的一个异常类,而作为异常,必然就是期待着被捕获的,也就是说,在这个queryUser的外围,必然会有一个try-catch块来对这个异常进行捕捉,一旦这个异常被触发了,那么进入catch块就是不可避免的事了(当然我不太喜欢一直抛到main函数外面这种有点野蛮的做法...)
[贰] 场景重现:登陆注册
好了,让我们回归一个非常常见的场景:
我们暂且假设你现在需要在一个service方法中完成用户注册的流程,其中需要判断用户名是否存在,如果不存在则继续进行正常注册流程。
public void doRegister(UserVO user) throws Exception{
UserVO userFromDB = null;
try{
userFromDB = userDao.queryUser(user.getAccount());
}catch(Exception e){
//对异常进行处理,如进行调用监控系统或日志进行异常监控与记录
//甚至如果你可以认为这里出现的是系统级的异常,如数据库连接失败等
//你可能会采取 throw e ①
}
if(userFromDB!=null){
//对于用户名已存在进行处理,可以抛出一些自定义异常
//或者其他返回信息处理方式
}
//...流程继续...
}
如果queryUser方法未找到时你返回使用的是null,那么恭喜你,这里一切正常。
但如果是抛出异常,那就要注意了。甚至你确实使用了①处的方式,那就危险了——因为一个NoFind异常足够让你整个流程中断。本来你的想法是“没有找到用户”,也就是用户为null时正好是程序往下进行的条件,结果很不幸被你捕获了,中断了。
那么抛出异常,但不用① 处的方法呢,没错,程序会正常运转,但是这有一个优化上的问题:我们往往在try-catch中希望捕获到的是系统级异常,比如数据库链接、操作失败,远程访问超时,IO操作失败等,这些失败的情况往往是我们需要运用监控系统进行监控,以时刻掌握整个系统平台运行的状态。甚至在有些业务中,我们需要对这些系统造成的错误使用人工方式进行处理(比如自动下发短信超时,这个时候需要客服为客户进行手动下发,甚至电话通知等)。所以说,对于一个简单的NoFound,如果我们也把它作为这种等级的异常进行一并捕获,这是一个有些多余、甚至错误的做法。
请你只在面对“出乎程序可预料”的行为时,才使用异常。①
[叁] 方案建议
综上所述,我们需要对"NoFind"现象以一种统一的方式进行表示,这个特别是在一个团队内部,一定要有一个统一的规范。可以保持使用null,不管对于单独的VO还是List,只要数据库中查询无数据,一概返回null值。将null作为"NoFound”的标识,相信也是绝大部分的程序员最习惯的做法。当然在CleanCode(《代码整洁之道》)一书中作者提出不要返回null值,而返回一个“空值对象”的方案,当然也是有道理的,但是这对于大部分程序员来说,恐怕并不习惯吧。
① Practical Java, P87