如何评价阿里近期发布的Java编码规范?

【Java编码规范】《阿里巴巴Java开发手册(正式版)》发布! - 掘金 用于Android开发是否有借鉴意义?其中是否有各位经常违反的点?用于公司…
关注者
5,052
被浏览
775,273

123 个回答

关于这个手册:

正在阅读中,读完再更新。

编码规范这种事情,从来都是有主观有客观的。

像缩进、换行、命名之类的事情就是纯主观,在团队开发中就应该硬性统一,以便协作顺畅。这是个程序员容易发挥主观能动性的地方,然而这地方并不适合发挥那么多主观能动性…

所以像这个开发手册的“命名规约”部分,我觉得就是很合理的,并没有什么槽点可吐——这就是这个团队做出的约定,喜欢不喜欢照办就是。其它团队选用其它的约定也是很合理的事,只要能贯彻一致性就好。

--------------------------------------

引用几个点来举例:

12. 【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。

这个是属于通用的良好的Java代码风格。Java的interface声明的成员方法,默认就是public abstract的,所以这两个修饰符不写出来就以及是最简洁明了的写法;而interface里声明的“变量”默认就是public static final的——只能声明公有常量,所以这些修饰符也是不要写出来最简单明了。

相关信息:

  • JLS8 9.3:
    • Every field declaration in the body of an interface is implicitly public, static, and final. It is permitted to redundantly specify any or all of these modifiers for such fields.
  • JLS8 9.4
    • Every method declaration in the body of an interface is implicitly public (§6.6). It is permitted, but discouraged as a matter of style, to redundantly specify the public modifier for a method declaration in an interface.
    • An interface method lacking a default modifier or a static modifier is implicitly abstract, so its body is represented by a semicolon, not a block. It is permitted, but discouraged as a matter of style, to redundantly specify the abstract modifier for such a method declaration.

这种风格建议是写在Java语言规范里的。没啥需要争论。

--------------------------------------

另一点:

8. 【强制】POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。
反例:定义为基本数据类型 boolean isSuccess;的属性,它的方法也是 isSuccess(),RPC框架在反向解析的时候,“以为”对应的属性名称是 success,导致属性获取不到,进而抛出异常。

虽然也是主观规定,但这是阿里系的Java代码的惨痛的经验教训。对阿里系的代码来说,总之遵循的话可以减少灵异状况的发生,所以放在这个上下文里也是很合理的。

这个可以说是在用规范来规避团队里常用的库的坑。这坑的解决办法可以是修改常用库里的逻辑来尽可能“聪明”地识别情况,也可以靠这样的规范。某种意义上说规范是从上游卡住了问题的发生,比起把下游处理弄复杂要更干净一些吧。

--------------------------------------

关于“(三)格式规约”,要是能来个附件弄个分别适用于IntelliJ与Eclipse JDT的格式规则文件就好了。各位阿里系Java码农就不用烦了,每次写完代码自动格式化一下就完事…

--------------------------------------

关于final与效率…

18. 【推荐】final 可提高程序响应效率,声明成 final 的情况:
1) 不需要重新赋值的变量,包括类属性、局部变量。
2) 对象参数前加 final,表示不允许修改引用的指向。
3) 类方法确定不允许被重写。

这…这三个具体项写得都挺好,但原因却不太对。

  • 所有JVM都相关:方法参数与局部变量用final修饰是纯编译时信息,到Class文件里就已经没有踪迹了,JVM根本不会知道方法参数或者局部变量有没有被final修饰。这个是完全不可能影响性能的。
  • HotSpot JVM相关:
    • static final常量是好的,HotSpot VM里的JIT编译器会利用这个信息来做优化;
    • 实例变量(字段)用final修饰的话,目前(直到最新的JDK8u)的HotSpot VM都不会使用这个信息来优化。只有JDK自身的一些核心类被区别对待(例如说java.lang.String上的final成员)。所以这个也不会影响性能。
    • 实例方法用final修饰:其实在可以使用final修饰的场景下,就算没有用final修饰,HotSpot VM的JIT编译器也一样会通过类层次分析(Class Hierarchy Analysis,CHA)来发现这个方法是实质上只有一个实现版本的,所以最终生成的代码质量会一样,性能不会有区别。而在不能使用final修饰的场景下(例如说这个方法真的有多个实现),如果当前加载的类中其实只有单一实现(别的实现在尚未加载或已经卸载的类中),则HotSpot VM仍然可以通过CHA把它当作单一实现来做激进优化,并通过deoptimization来保证安全。所以在HotSpot VM的环境中,其实实例方法没有必要为了性能而加final,而是真的希望表达语义上的限制(例如java.lang.getClass()是一个不允许覆写的方法,加final修饰就很合理)时才应该使用。

我在淘宝写Java代码的时候,有一段时间确实也是习惯在可以加final的地方(包括方法参数与局部变量)都加final,但并不是为了性能好,而是为了提醒自己以及阅读/修改我的代码的同事们,这个参数/变量的值在这个方法里真的是不应该修改,并且让Java编译器去检查到底是不是没有被乱修改。

后来我的代码风格稍微变了一点,用Eclipse插件来生成那些final出来,我就不用手写了…例如说用Project Lombok里的邪恶的

val

来声明局部变量(逃

--------------------------------------

6. 【推荐】尽量不要在 vm 中加入变量声明、逻辑运算符,更不要在 vm 模板中加入任何复杂的逻辑。

哈哈哈这个不知道有多少非阿里系的同学能看懂是什么的。

这说的是Velocity模版嗯…阿里系的Java码农多半都知道。

==========================================

关于这个问题下的其它一些回答:

如何评价阿里近期发布的Java编码规范? - qwertyuiop的回答 - 知乎

这个回答说的肯定是我咯。我是偶尔会在回答里提及鄙司Azul Systems的产品,例如主打的高性能JVM——Zing,以及Azul提供技术支持的OpenJDK发行版——Zulu。并没有Zule这个产品,而且我也从来没给Zulu的编译器打过广告>_<

我在Azul主要参与的是Zing里的编译器研发嗯。偶尔说说Zing JVM的GC或者编译器。更多的还是聊大家更常用的Oracle JDK / OpenJDK的HotSpot VM的状况。其实我也不是只聊Java话题的,感兴趣的话可跳转送门:

[资料合集] RednaxelaFX写的文章/回答的导航帖(work in progress)

这个回答提到的我的回答,是这个:

一般编程语言都是英文的,大家对中文编程有什么样的看法,中文编程有哪些优劣势? - RednaxelaFX 的回答 - 知乎
另外⋯想起以前在淘宝的某业务组轮岗时的体会:
那边某些代码充斥着英文、拼错的英文、汉语拼音、拼错的汉语拼音,全部混在一起看得我头昏脑胀。我实在受不了了,于是给他们写的单元测试一概用了汉语方法名,例如:
@Test
void 测试提交订单() {
  // ...
}
这样JUnit跑出来的结果就都是正统汉字了。再见,恶心的拼写错误(逃
(不过我轮岗结束后那些汉语方法名应该又都被干掉了哈哈哈哈)

简单说说这段文字的背景:

  • 那是2010年的事了。我那个时候在淘宝运营支撑部的平台架构组。我们会偶尔轮岗到别的业务组去体验一下业务组的实际工作状况和需求,以便更好的服务业务组。不敢说轮岗过去能帮上多少忙,不乱帮倒忙就不错了。总之最低限度我们得体会一下业务组到底有哪些需求,有哪些痛点需要解决。
  • 有一次我轮岗过去的组里的某些代码确实是让我震惊了。不但有超过一千行的单个方法,没人敢重构,而且还有前面引用的回答说的,“英文、拼错的英文、汉语拼音、拼错的汉语拼音”。确实会有时候一个上层接口的实现调用下层接口的方法,上层可能是getDiscount(),下层却是getDaZhe()…
    • 但那块代码并不是我所知道的淘宝的代码的典型状况。从我的角度看,那段代码确实是很不好,但是是个例而不是常态。我当时接触的业务代码不是特别多,平时接触的中间件(例如HSF)和通用服务,还有我自己写的Impla 2.x / 3.x啥的,里面的代码都比较干净,各种命名都比较统一,并没有上面引用的回答所说的情况。
    • 我当时只是把JUnit的测试用例里面的用例方法给改成用中文名了。主要是想行为艺术一下提醒那边的同事他们原本的命名实在太糟糕了,顺带演示一下Java源码支持Unicode的好处/用处。
    • 如果我记得没错的话,我轮岗结束后没多久,那些被我改成中文名的方法就又被改回用英文了。希望后来他们用的名字是更统一的吧…
  • 后来我也有过一次轮岗到业务组参与写业务代码的经历。那次最初的代码里也有过奇怪的英文+拼音的混合物,但很快组里就有老大组织讨论统一了命名,给出了一个参考词汇表来保证大家的命名是一致的。这就比前面提到的那次轮岗的体验好很多。

正是因为阿里的Java代码里曾经有过这样的命名混乱的现象,即便它不是常态也是应该警觉和避免的,所以《阿里Java开发手册》里的这一条在这种上下文里就非常合理:


请不要曲解我的文字然后引用来说根本不相关的事情。谢谢。

我仔细看了一遍,觉得写得挺好的。之前不管是在Google、还是FB都会有在刚进去的时候接受公司培训,其中一部分内容就是代码规范和公司里常用的推荐写法。对比Google、FB的style guide,我先说说阿里Java规范文档中的亮点;为了完整性,答案最后也附带我对于这个文档的吐槽点:

亮点如下:

1.

这点之前FB的java代码里大量使用guice相关的Dependency Injection,所以不断被我们Tech Lead来强调。很支持这一点。所以不管你在大公司还是创业公司,这点强烈推荐。

2.

这个表格非常好;其实我现在年纪大了,这些细节的地方经常忘记。有这么一个极简的提醒手册很有帮助。

3.

这个对于初学者也很有帮助。不过建议把最开始的“不要在”修改成“不能在”。同时建议这里应该附加解释下: fast fail vs fail safe iterantion。

4.

这段非常同意。

5.

这段笑喷了。的确我也支持在注释(特别是大量业务逻辑说明)的时候,大胆使用中文!另外commit message里面最好也大胆使用中文。

6.

很简洁很清晰的图。

7.

规范最后对于服务器上设置 TCP time_wait、调节file descriptor数量,还有 JVM上臭名昭著的 OOM 问题的建议都很不错。

总结来看从开源项目,行业会议,到今天的这个Java规范,都反复证明了阿里还是一如既往地回馈整个技术社区。特别对于国内的开发者帮助很大。推荐大家都去读一下:pan.baidu.com/s/1o8RSla

最后说说规范文档里我不爽的地方:

1. 在2017年,网页、wiki、markdown或者说github页面高度发达的今天,不知道为什么阿里没有选择将规范文档放在网页上,而是用PDF文档直接抛出来,让人费解。文档还被加上重重的水印,好似怕别人剽窃里面的内容一样,感觉像是10年前的小农思维。用PDF文件对于后续的文档改进和版本管理都会是大问题。建议做成类似:Google Java Style Guide

2. 文档里规定了代码的格式如下:

强制: if / for / while 后面都有空格, { 前面要有空格等:

但是在文档后续的代码里,这些规范压根没有被遵守(自己打脸?)







看来文档的用心程度还要进一步提高呀!

3. 文档里建议用4空格缩进。这点之前说过很多次,在硅谷的程序圈差不多10年前已经逐渐从4空格缩进改为2空格缩进,所以国内这一块最好能够与时俱进。主要4空格缩进在稍微逻辑复杂一点的代码里,就直接超过120行的宽度限制了。参考:Google Java Style Guide




4. 建议多加上对于 java 8 的支持和说明,同时可以多借鉴guava的一些写法:比如对于array set dictionary 的操作,和 filter map 以及 closeable ,外加java8的 lambda。