第七章 错误处理
7.1 使用异常而非返回码
如果给调用者返回某些错误码,可能会导致代码凌乱不堪,我们应该用异常去替代错误码
作者给出的例子就不说了,使用异常后,我们的代码整洁了许多。。。
7.2 先写Try-Catch-Finally语句
在编写可能抛出异常的语句时,最好先写出try-catch-finally语句
7.3 使用不可控异常
可控异常的代驾就是违反开放、闭合原则,对于一般的应用开发,我们尽量使用不可控异常
7.4 给出异常发生的环境说明
应创建信息充分的错误消息,并和异常一起传递出去
比如失败的操作、类型。如果系统有日志系统,传递足够的信息出去
7.5 依调用者需要定义异常类
作者给出了一个例子,这是我们对某个第三方库代码的调用:
ACMEPort port = new ACMEPort(12); try { port.open(); } catch (DeviceResponseException e) { reportPortError(e); } catch (ATM1212UnlockedException e) { reportPortError(e); } catch (GMXError e) { reportPortError(e); } finally { ... }
以上代码包含了一大推重复代码,我们可以通过打包调用API,来确保他返回一个通用的异常
简单来说就是,因为上面那么多异常最后处理的代码都一样,我们可以把这么多个异常整合成一个异常:
ACMEPort port = new ACMEPort(12); try { port.open(); } catch (DeviceResponseException e) { reportPortError(e); } finally { ... } public class LocalPort { private ACMEPort port; public LocalPort(int number) { port = new ACMEPort(number); } public void open() { try { port.open(); } catch (DeviceResponseException e) { throw new PortDeviceFailure(e); } catch (ATM1212UnlockedException e) { throw new PortDeviceFailure(e); } catch (GMXError e) { throw new PortDeviceFailure(e); } finally { ... } } }
将第三方API打包是一个良好的时间手段,当我们打包一个API的时候,就降低了对它的依赖,未来可以不用太痛苦的改用其他代码库
我的经验之谈:
之前的项目在用Glide,一开始大家写的时候,就开始链式调用往代码里侵入,后来大家都开始这么写了。直到有一天,Glide和另一个三方库有冲突了,必须升级Glide,而且Glide新版本做了很多的优化,也比较吸引我们去升级。但是新版本调用方法跟旧版本不一样,升级以后所有调用Glide的地方全部飚红,一共几百个类,最后是我来完成这项任务的,那叫一个痛苦,最关键的是干这种事的时候就会觉得自己特别傻X,后来改完以后,项目多了一个ImageUtils的工具类,封装了相关图片加载的方法。所以啊,当这节说到这个打包第三方API,真的是经历过的人,经历过这种痛苦的人,才明白打包有多重要。我们加了一个ImageUtils工具类,可以避免现在使用的Glide的API的升级导致整个项目大改动,或者某一天出了一个更加出色的图片加载库,我们可以很轻松的就替换掉了。再比方说,我们的项目要集成IM,当时是选择的网易云信,像这种第三方API也应该打包起来。如果日后我们不再使用网易云信,而换了其它的第三方库,因为侵入更少,我们可以更方便的去替换
7.6 定义常规流程
创建一个类或者配置一个对象,用来处理特例,异常行为被封装到特列对象中
7.7 别返回null值
作者在这里举了一个列子,代码中对每一个对象都进行了判空,充满了大量的判空方法,代码看起来很糟糕
如果我们打算在方法中返回null值,不如抛出异常,或者返回特例对象
如果在调用某个第三方API中可能返回null值的方法,可以考虑用新方法打包这个方法,在新方法中抛出异常或者返回特例对象
比如getEmployees()要返回一个List,那么我们可以抛出异常或者返回一个空列表,那么调用者不需要判空,这样代码更整洁了
7.8 别传递null值
有人传入null值,我们就需要在方法内部进行判空、或者抛出异常、或者使用断言
以上方法都不是完美的解决方法,最恰当的方法就是禁止传入空值
当然在实际情况中我们不可能完全避免这种情况,比方说请求网络的方法可能会返回null值,这就必须要进行判断了
我们可以加上注解@Nullable和@Nonnull来提示调用者,是否可以传入空值
7.9 小结
整洁代码是可读的,但也得强固
将错误处理隔离看待,独立于主要逻辑之外,就能写出强固而整洁的代码
总结
其实我发现大部分人写代码的经历是这样的:
一开始是啥也不懂,啥也不写,代码各种报错,各种空指针,显得特别低级
觉得好代码就是健壮的,强固的,代码各种判空,觉得这样就是高手,从不在乎是否是整洁的,可读的
开始理智的选择使用判空return或者抛出异常,断言,注解等,知道如何在健壮和整洁之间进行平衡