null值
对于nil值,应该思考nil对于业务意味着什么,如果就应该nil业务就不该继续,直接抛异常,没必要去check nil的值,调用时自动会抛异常;而如果值为nil需要做分支流程,当前程序还要继续下去,那就需要判空。
不应该判断的例子:
比如jdk的java.util.Properties#load(java.io.Reader)方法,对入参就不需要判空,因为如果是空也没法处理,只能抛出NPE,不判空等价于加入了判空代码:
if(null == o) { throw new NullPointerException(msg) }
- 比如下面的Spring的EmbeddedWebApplicationContext#getEmbeddedServletContainerFactory代码,并没有对getBeanFactory()的返回值进行判断
String[] beanNames = getBeanFactory() .getBeanNamesForType(EmbeddedServletContainerFactory.class);
这些例子都有一个共性,面对null值程序没法继续往下走,必须要抛异常。没必要判断,或者可以判断后抛出自己想抛的异常。如果进行判断,代码反而显得多余。
对于最终的异常,在提供接口给他人调用的顶层(比如Controller层或RPC调用的Service层)catch住,以错误码形式返回就好了
应该判断的例子:
- jdk的java.util.Objects#equals方法java.util.Objects#hashCode, Spring中的好多方法,比如:org.springframework.web.context.ContextLoader#loadParentContext
- 比如你去DB查询用户信息返回数据为User a=null,如果不为空你需要获取a.name并展示到前端,如果为空你就不展示,这个例子中就是应该判断null值的。
应该判断的例子都有一个共性,null值是正常的情况
参数传递与返回值
- 一些公共的参数应该通过Filter添加到ThreadLocal对象中,而不应该所有方法都不断传递这个对象。比如userId, 设备的ttid,utdid,国际化语言等。需要的时候从ThreadLocal取,而不是通过参数传递给被调用方法
- 参数传递应该尽量符合最小需要原则,尽量使用明确简单的类型。不要使用Map,Json等可读性较差的类型。
- 返回值符合最小需要原则,无用的字段不要返回,造成阅读负担。不要使用Map,Json等作为返回值
参数校验
- 参数校验只在提供接口给别人的公共入口处校验,不需要重复校验。如果存在重复校验,则不合理,需要有一个收口的地方。
- 对于调用别人的接口,要立即对返回值进行校验,而不应该延迟到将这个返回值作为入参调用方法。
- 自己调用自己的内部方法可以不进行校验,但是如果要提供给他人调用,一定要对他人的参数进行校验
异常与错误
- java里面的Exception的定义:
* The class {@code Exception} and its subclasses are a form of
* {@code Throwable} that indicates conditions that a reasonable
* application might want to catch.
- java里面Error的定义:
An {@code Error} is a subclass of {@code Throwable}
* that indicates serious problems that a reasonable application
* should not try to catch. Most such errors are abnormal conditions.
* The {@code ThreadDeath} error, though a "normal" condition,
* is also a subclass of {@code Error} because most applications
* should not try to catch it.
简而言之,异常是程序员可能希望catch住然后恢复的,而错误是不可恢复的
- go语言的error是指程序可以恢复的错误,异常是系统不可恢复的。go的error类似java里面的Exception,但是不一样,go的error是通过返回值直接返回的,个人比较喜欢这种方式。
- 优点:不需要担心调用的方法抛出个RuntimeException, 有什么error就直接判断返回值就知道了,而不需要catch异常做分支判断。
- 缺点:如果调用嵌套层数比较深,每一次调用都判断是否返回error,相比再最外层调用直接catch异常会显得很繁琐
我觉得go语言的错误处理比java的好很多,如果错误是可恢复的,就应该在返回值里直接返回错误,而不应该throw在catch去走控制流程。而且catch的写法相当不优雅,没有直接返回值返回优雅。