很好的东西,98%以上内容可以直接使用。有一些地方个人觉得可以提高一下:
1. 应该写明这个文档是什么许可协议的。比如说在其基础上稍作改动然后分发是否许可?如果没任何许可协议,那有人把其中大括号必须不换行改成必须换行然后用原来的名字免费分发你也拿他没办法吧。
2 “变量"跟“常量”的用法有点混淆
12.【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁 性,并加上有效的 javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是 与接口方法相关,并且是整个应用的基础常量。
初看时一头雾水,根本就不可能在接口里定义变量呀,看后面的描述,似乎说的是常量。类似的地方好像还有几处,感觉就是对象域,类静态域(不管是否final),方法的局部变量,都统称变量。当然,这是小节,习惯了就好。只不过一开始看时感觉有点混乱。
3. 作为本质上是“开发规范”的“开发手册”,个人认为一些根本过不了编译的“知识点”就不需要列出来了,有点干扰重点。比如说:
6. 【强制】泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方法。 说明:苹果装箱后返回一个<? extends Fruit>对象,此对象就不能往里加任何水果,包括苹 果。
这种写法根本过不了编译,个人感觉没必要专门写出来。何况提到了<? extends T>不能.add,为何不提<? super T>不能.get。
4. 关于包名的规则我觉得不能涵盖所有场景
9. 【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式。 正例: 应用工具类包名为 com.alibaba.mpp.util、类名为 MessageUtils(此规则参考 spring 的框架结构)
如果包名的一部分是个词组怎么办?比如说Unit of work。难道写成 com.alibaba.unit.of.work ? 如果刚好又有个叫 com.alibaba.unit的包,那 com.alibaba.unit.of.work就会在com.alibaba.unit下面,虽然它们之间毫无逻辑关系。我记得行业标准是包名用小写加下划线的分割方式,例如 com.alibaba.unit_of_work 。
5. 有一些原因解释没触及痛点(也就是规则我是认同的,但由于原因解释没触及痛点,反而会被一些“独立思考能力强”的同学驳回来)。
比如说:
18.【推荐】final 可提高程序响应效率,声明成 final 的情况: 1) 不需要重新赋值的变量,包括类属性、局部变量。 2) 对象参数前加 final,表示不允许修改引用的指向。 3) 类方法确定不允许被重写。
在现代主流javac和JVM下“final可提高程序响应效率”并不成立。加final更多的是从可读性方面去考虑的。主要的关键在于,如果开发时如果习惯使用IDE的“抽取局部变量”功能来声明局部变量,那么加final根本没有额外工作量,属于“何乐而不为”。而如果程序员的习惯是手打变量声明,那在变量声明处打final就太烦人了,根本不可能推得动。
再比如说:
1. 【强制】不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如: IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查来规避,保 证程序健壮性。 正例:if(obj != null) {...} 反例:try { obj.method() } catch(NullPointerException e){…}
这点实在不能同意更多,问题是痛点不是“健壮性”(这种提法又落入“防御式编程” vs “契约式编程” 的争吵中,谁说try...catch...不健壮的。)事实上对RuntimeException不做规避的最大问题是:
第一,会干扰使用“异常断点”来排错。
第二,导致维护时难以判断开发时的原始意图。比如说,这样一段代码:
public void setEmployeeBirthday(Long id, Date birthday) { final Employee employee = loadEmployee(id); employee.setBirthday(birthday); }
在setBirthday那行抛了NPE,那么其实有以下几种可能:
1. loadEmployee返回null是一种正常情况,写上面这个方法的程序员忘了处理这种正常情况。
2. loadEmployee在任何时候都不应该返回null。
3. loadEmployee返回null是一种正常情况,但在当前特定的场景下,传入的id不应该令它返回null。
如果写这个方法时,在loadEmployee之前检查了id,我维护时立马就可以判定这是第三种情况。如果在loadEmployee之后使用之前就检查了employee是否null而没有在之前检查id,我立马就可以判定这是第二种或第三种情况。如果所有程序员都严格遵守这个“尽可能检查” 的规则,我发现根本没有检查代码,基本就可以判定写这段程序的程序员完全没想到employee可能是null这种场景(也就是很可能是第一种情况)。
6. 还有一些我能明白作者要传达的意思,但措辞上似乎有点混乱的。比如说
9. 【推荐】谨慎地记录日志。生产环境禁止输出 debug 日志;有选择地输出 info 日志;如果使 用 warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘 撑爆,并记得及时删除这些观察日志。 说明:大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。纪录日志时请 思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?
这是从运维的角度来说的吧?从编程角度来说,打日志还真是越多越好,只要level不乱就行。我在日常工作中是直接把TDD里测试代码的步骤拿过来做trace日志的,在生产环境排错时不知救过多少次命。其实关键问题是,trace log本来就是用来排一些debug都查不出的错的,如果我能在开发时预判这里可能有问题,我就应该打debug level的log了。如果要求程序员在“记录日志时”预判某条日志能不能给排错带来好处,那trace level的log就基本不用打了(明知能给排错带来好处的log当然得打debug level)。
总体来说,这个文档是对初学者非常有用的参考资料,至少有一些平时两种方案摇摆不定的地方(嗯,大括号,说的就是你),你现在可以明确知道其中一种是经过实践验证绝无问题的(不代表另一种会有问题),也能避免不少运行时的坑。以上几点只是结合个人经验的一点点补充意见。
本人前阿里员工,这确实就是阿里日常的编码规范,每一条都是前人踩过的坑,通过血的教训总结出来的。
能公布出来真是造福全部Java开发者。
如果你在经历Java项目,尽管拿去用,会少踩很多坑。
如果你开始新项目,请不要使用一种需要32页编码规范才能少踩坑的语言。