1、 异常分类
a. RuntimeException 及其子类不要求捕捉,而其它的异常要求捕捉随便举几个 RuntimeException 子异常,有:数组越界异常、空指针异常、0作除数异常
b. 非RuntimeException 异常有:Socket异常、IO异常等
c. 对比一下我们就会发现,RuntimeException 是在程序中可以完全避免的,
c1. 比如数组越界异常,只要我在程序里作个判断,如果要访问的数组元素下标和数组的长度作一下比较就知道会不会越界,
c2. 再比如空指针异常,如果在访问对象时判断一下对象的变量是否为空就可以了。
c3. 而非RuntimeException 则是程序无法避免的,比如IO异常,你的程序正在读一个文件,而这个文件所在磁盘出现了坏道,这就必然会引发IOException,这是不是靠编程高手编写完美的程序就可以法避免得了的,程序所能做的只有出现异常之后怎么处理的问题
2、 异常处理原则之一:延迟捕获
意思是,当异常发生时,不应立即捕获,而是应该考虑当前作用域是否有有能力处理这一异常的能力,如果没有,则应将该异常继续向上抛出,交由更上层的作用域来处理。
一个例子:某方法String readFile(String filename),会去尝试读出指定文件的内容并返回,其使用FileInputStream来读取指定文件,而FileInputStream的构造方法会抛出FileNotFoundException,这是一个Checked Exception。那么readFile方法是应该捕获这个异常,还是抛出这个异常呢?很显然应该抛出。因为readFile这个方法可能会在不同的场景下,被不同的代码调用,在这些场景中,出现“文件未找到”的情况时的处理逻辑可能是不同的,例如某场景下要发出告警信息,另一场景下可能会尝试从另一个文件中读取,第三个场景下可能需要将错误信息提示给用户。在这种情况下,在readFile方法内的作用域中,是处理不了这个异常的,需要抛出,交由上层的,具备了处理这个异常的能力的作用域来处理
在服务器端和dao段 都是抛出异常很少有捕获 捕获在客户端 因为不能吧异常抛给用户。从dao到服务器到客户端都是层层抛出 到客户端必须捕获处理 可能给用户提示信息和给程序猿一些记录 方便交互和处理
3、 个人经验
a. 以MVC框架为例,首先controller层必须捕获异常,一般情况下不允许将系统内部的异常不做任何封装处理直接抛给客户端,这样对系统来说会暴露过多信息(异常栈信息都抛给客户端,可能会把SQL结构都抛出去),这是不安全因素;同时,这对用户来说也是不友好的,让一些不搞计算机的同学看得一头雾水,并且怀疑网站的正规性与可靠性,可能会直接影响未来的业务。因此,通常需要在controller层封装一些错误码、用户友好语句(系统维护中等等欺骗用户的语句)。当然这也不是铁律,有的时候需要后台进行较检,可能需要抛出一些非公用的异常信息到界面上,比如之前车贷项目中用到过的上传附件检查,就是用后台异常提示用户的。还有就是传入参数有效性较检,也需要抛出自定义异常(用@NotNull或NotEmpty等注解抛出message信息),即不需要捕获后统一处理返回到用户界面
b. 接着说一下service层,业务层需要处理业务逻辑,当然这一层不需要考虑参数是否为空,需要考虑的仅仅是从dao查询相关的异常,比如ConnectionExcepion、SQLException、BadSQLGrammerException等异常,这些异常我通常会去捕获,因为这些异常涉及到业务逻辑是否能正常执行,确实是service该考虑的事。做法是try…catch…捕获所有Exception,然后在catch中用logger.error(“message, e = {}”, e.getMessage())记录日志信息,用于到时候定位error代码位置,然后根据实际业务情况,看这个异常捕获后是否需要用一个异常封装一下信息抛给控制层,然后由控制层统一处理也好或者直接返回给用户)。注意,业务层异常不是用于捕获NullPointerException等无聊的代码健壮性问题,这些都是业务层代码逻辑没控制好,查询值需要做一下非空判断
c. 至于dao层,我想没必要说了吧,一般我也不写实现类,持久层的mybatis3.X都用动态代理来实现了。 BTW,try…catch最好有针对性,不要把所有美容都放到try里面检查是否有异常,一般来说总有些无关异常的语句可以提到异常块之外,反正能提就提。