点评阿里JAVA手册之编程规约(命名风格、常量定义、代码风格、控制语句、注释规约)
下载原版阿里JAVA开发手册 【阿里巴巴Java开发手册v1.2.0】
本文主要是对照阿里开发手册,注释自己在工作中运用情况。
本文难度系数为一星(★)
第一篇 点评阿里JAVA手册之编程规约(命名风格、常量定义、代码风格、控制语句、注释规约)
第二篇 点评阿里JAVA手册之编程规约(OOP 规约 、集合处理 、并发处理 、其他)
第三篇 点评阿里JAVA手册之异常日志(异常处理 日志规约 )
第四篇 点评阿里JAVA手册之MySQL数据库 (建表规约、索引规约、SQL语句、ORM映射)
码出高效、码出质量。
代码的字里行间流淌的是软件生命中的血液,质量的提升是尽可能少踩坑,杜绝踩重复的坑,切实提升质量意识。另外,现代软件架构都需要协同开发完成,高效协作即降低协同成本,提升沟通效率,所谓无规矩不成方圆,无规范不能协作。众所周知,制订交通法规表面上是要限制行车权,实际上是保障公众的人身安全。试想如果没有限速,没有红绿灯,谁还敢上路行驶。对软件来说,适当的规范和标准绝不是消灭代码内容的创造性、优雅性,而是限制过度个性化,以一种普遍认可的统一方式一起做事,提升协作效率。
一、编程规约
(一) 命名风格
1. 【强制】 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
反例: _name / __name / $Object / name_ / name$ / Object$
【点评】规则好,严格遵循。
2.【强制】 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式,也要避免采用。
正例: alibaba / taobao / youku / hangzhou 等国际通用的名称,可视同英文。
反例: DaZhePromotion [打折] / getPingfenByName() [评分] / int 某变量 = 3
【点评】 规则好,严格遵循。
3.【强制】类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO
正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
【点评】 规则好,严格遵循。
4.【强制】方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从 驼峰形式。 正例: localValue / getHttpMessage() / inputUserId
【点评】 规则好,严格遵循。
5. 【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
正例: MAX_STOCK_COUNT 反例: MAX_COUNT
【点评】 规则好,在项目中严格遵循。
6.【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。
【点评】 规则好,严格遵循。
7. 【强制】中括号是数组类型的一部分,数组定义如下:String[] args; 反例:使用 String args[]的方式来定义。
【点评】 规则好,严格遵循。
8. 【强制】POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。 反例:定义为基本数据类型 Boolean isDeleted;的属性,它的方法也是 isDeleted(),RPC
框架在反向解析的时候,“以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异 常。
【点评】 存疑,如果数据库设计表字段,用is开头的数据库字段。 进入orm时一般自动生成实体类代码,很可能存在。自己在设计数据库时,多用flag结尾作为tinyint去表示布尔型,不存在此情况。
9.【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式。 正例: 应用工具类包名为 com.alibaba.open.util、类名为 MessageUtils(此规则参考 spring 的框架结构)
【点评】规则好,以后在项目中遵循。
目前代码中有com.wxhealth.util.wechat。
10.【强制】杜绝完全不规范的缩写,避免望文不知义。 反例: AbstractClass“缩写”命名成 AbsClass;condition“缩写”命名成 condi,此类 随意缩写严重降低了代码的可阅读性。
【点评】规则好,严格遵循。
11.【推荐】如果使用到了设计模式,建议在类名中体现出具体模式。 说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。
正例:public class OrderFactory; public class LoginProxy; public class ResourceObserver;
【点评】规则好,严格遵循。
12.【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。
正例:接口方法签名:void f();
接口基础常量表示:String COMPANY = "alibaba";
反例:接口方法定义:public abstract void f();
说明:JDK8 中接口允许有默认实现,那么这个 default 方法,是对所有实现类都有价值的默认实现。
【点评】规则好,很少注意到这样使用。
在interface里面的变量都是public static final 的。所以你可以这样写:
public static final int i=10;
或则
int i=10;(可以省略掉一部分) 注意在声明的时候要给变量赋予初值。
首先你要弄清接口的含义.接口就是提供一种统一的’协议’,而接口中的属性也属于’协议’中的成员.它们是公共的,静态的,最终的常量.相当于全局常量.
抽象类是不’完全’的类,相当于是接口和具体类的一个中间层.即满足接口的抽象,也满足具体的实现.如果接口可以定义变量,但是接口中的方法又都是抽象的,在接口中无法通过行为来修改属性。有的人会说了,没有关系,可以通过实现接口的对象的行为来修改接口中的属性。这当然没有问题,但是考虑这样的情况。如果接口A中有一个public访问权限的静态变量a。按照Java的语义,我们可以不通过实现接口的对象来访问变量a,通过A.a = xxx;就可以改变接口中的变量a的值了。正如抽象类中是可以这样做的,那么实现接口A的所有对象也都会自动拥有这一改变后的a的值了,也就是说一个地方改变了a,所有这些对象中a的值也都跟着变了。这和抽象类有什么区别呢,怎么体现接口更高的抽象级别呢,怎么体现接口提供的统一的协议呢,那还要接口这种抽象来做什么呢?所以接口中不能出现变量,如果有变量,就和接口提供的统一的抽象这种思想是抵触的。所以接口中的属性必然是常量,只能读不能改,这样才能为实现接口的对象提供一个统一的属性。通俗的讲,你认为是要变化的东西,就放在你自己的实现中,不能放在接口中去,接口只是对一类事物的属性和行为更高层次的抽象。对修改关闭,对扩展(不同的实现implements)开放,接口是对开闭原则的一种体现。转自:http://blog.csdn.net/ameyume/article/details/6189749
13. 接口和实现类的命名有两套规则:
1)【强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。
正例:CacheServiceImpl 实现 CacheService 接口。
2)【推荐】 如果是形容能力的接口名称,取对应的形容词做接口名(通常是–able 的形式)。
正例:AbstractTranslator 实现 Translatable。
【点评】规则好,使用中自己多这样使用:IApiProcessor
14.【参考】枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。 说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
正例:枚举名字:DealStatusEnum,成员名称:SUCCESS / UNKOWN_REASON。
【点评】规则好,严格遵循。
15.【参考】各层命名规约:
A) Service/DAO 层方法命名规约
1) 获取单个对象的方法用 get 做前缀。
【点评】规则好,严格遵循。
2) 获取多个对象的方法用 list 做前缀。
【点评】规则存疑 ,自己用get开头,实体类名加List 。getOrderListByName()
3) 获取统计值的方法用 count 做前缀。
【点评】规则存疑 ,自己用get开头,实体类名加Count 。getOrderCountByName()
4) 插入的方法用 save(推荐)或 insert 做前缀。
【点评】规则好,严格遵循。
5) 删除的方法用 remove(推荐)或 delete 做前缀。
【点评】规则好,严格遵循。
6) 修改的方法用 update 做前缀。
【点评】规则好,严格遵循。
B) 领域模型命名规约
1) 数据对象:xxxDO,xxx 即为数据表名。
2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
3) 展示对象:xxxVO,xxx 一般为网页名称。
【点评】规则存疑,数据对象,一般与表名相同,可能命名不同。数据对象也同时用于数据传输对象和展示对象。
4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
【点评】规则好,严格遵循。
(二) 常量定义
1. 【强制】不允许任何魔法值(即未经定义的常量)直接出现在代码中。
反例: String key = "Id#taobao_" + tradeId;
cache.put(key, value);
【点评】规则好,在代码中尽量遵循。但是,我们在api接口中有:
Map<String, Object> response = new HashMap<String, Object>();
response.put("code", 0);
response.put("orderflag", orderflag);
将response转化为json格式,通过rest api发布。
2. 【强制】long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟数字 1 混淆,造成误解。
说明:Long a = 2l; 写的是数字的 21,还是 Long 型的 2?
【点评】规则好,代码中遵循。
3. 【推荐】不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。
如:缓存 相关的常量放在类:CacheConsts 下;系统配置相关的常量放在类:ConfigConsts 下。
说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
【点评】规则好,代码中遵循。
4. 【推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包 内共享常量、类内共享常量。
1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。
2) 应用内共享常量:放置在一方库的 modules 中的 constant 目录下。
反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示 “是”的变量:
类 A 中:public static final String YES = "yes";
类 B 中:public static final String YES = "y"; A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致线上问题。
3) 子工程内部共享常量:即在当前子工程的 constant 目录下。
4) 包内共享常量:即在当前包下单独的 constant 目录下。
5) 类内共享常量:直接在类内部 private static final 定义。
【点评】规则存疑,直接在类中定义跨应用共享常量。如Integer.MAX_VALUE 和Integer.MIN_VALUE。
public final class Integer extends Number implements Comparable<Integer> {
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/
public static final int MIN_VALUE = 0x80000000;
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
public static final int MAX_VALUE = 0x7fffffff;
。。。。。。
5. 【推荐】如果变量值仅在一个范围内变化,且带有名称之外的延伸属性,定义为枚举类。下面 正例中的数字就是延伸信息,表示星期几。
正例:public Enum { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);}
【点评】规则好,已经遵循。
(三) 代码风格
1. 【强制】大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;
如果是非空代码块则:
1) 左大括号前不换行。
2) 左大括号后换行。
3) 右大括号前换行。
4) 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。
【点评】规则好,已经遵循。
2. 【强制】左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格。
详见第 5 条下方正例提示。
反例:if (空格 a == b 空格)
【点评】规则好,已经遵循。
3. 【强制】if/for/while/switch/do 等保留字与括号之间都必须加空格。
【点评】规则好,未遵循。 在Eclipse下面,用格式化代码ctrl+shift+f 是这样的。
4. 【强制】任何二目、三目运算符的左右两边都需要加一个空格。
说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等。
【点评】规则好,未遵循。 在Eclipse下面,用格式化代码ctrl+shift+f 是这样的。
5. 【强制】缩进采用 4 个空格,禁止使用 tab 字符。
说明:如果使用 tab 缩进,必须设置 1 个 tab 为 4 个空格。
IDEA 设置 tab 为 4 个空格时, 请勿勾选 Use tab character;
而在 eclipse 中,必须勾选insert spaces for tabs。
正例: (涉及 1-5 点)
public static void main(String[] args) {
// 缩进 4 个空格
String say = "hello";
// 运算符的左右必须有一个空格
int flag = 0;
// 关键词 if 与括号之间必须有一个空格,括号内的 f 与左括号,0 与右括号不需要空格
if (flag == 0) {
System.out.println(say);
} // 左大括号前加空格且不换行;左大括号后换行
if (flag == 1) {
System.out.println("world");
// 右大括号前换行,右大括号后有 else,不用换行
} else {
System.out.println("ok"); // 在右大括号后直接结束,则必须换行
}
}
【点评】规则好,已经遵循。
6. 【强制】单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
1) 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
2) 运算符与下文一起换行。
3) 方法调用的点符号与下文一起换行。
4) 在多个参数超长,在逗号后换行。
5) 在括号前不要换行,见反例。 正例:
StringBuffer sb = new StringBuffer();
//超过 120 个字符的情况下,换行缩进 4 个空格,并且方法前的点符号一起换行 sb.append("zi").append("xin")...
.append("huang")...
.append("huang")...
.append("huang");
反例: StringBuffer sb = new StringBuffer();
//超过 120 个字符的情况下,不要在括号前换行
sb.append("zi").append("xin")...append
("huang");
//参数很多的方法调用可能超过 120 个字符,不要在逗号前换行
method(args1, args2, args3, ...
, argsX);
【点评】规则好,已经遵循。
7. 【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。 正例:下例中实参的"a",后边必须要有一个空格。 method("a", "b", "c");
【点评】规则好,已经遵循。
8. 【强制】IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式, 不要使用 windows 格式。
【点评】规则好,已经遵循。
9. 【推荐】没有必要增加若干空格来使某一行的字符与上一行对应位置的字符对齐。
正例: int a = 3;
long b = 4L;
float c = 5F;
StringBuffer sb = new StringBuffer();
说明:增加 sb 这个变量,如果需要对齐,则给 a、b、c 都要增加几个空格,在变量比较多的情况下,是一种累赘的事情。
【点评】规则好,已经遵循。
10. 【推荐】方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行。
说明:没有必要插入多个空行进行隔开
【点评】规则好,已经遵循。
(四) 控制语句
1. 【强制】在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程 序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且 放在最后,即使它什么代码也没有。
【点评】规则好,严格遵循。
2. 【强制】在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码,避免使用 单行的形式:if (condition) statements;
【点评】规则好,严格遵循。
3. 【推荐】表达异常的分支时,少用 if-else 方式,这种方式可以改写成:
if (condition) { ... return obj; }
// 接着写 else 的业务逻辑代码; 说明:如果非得使用 if()...else if()...else...方式表达逻辑, 【强制】避免后续代码维护困难,请勿超过 3 层。
正例:逻辑上超过 3 层的 if-else 代码可以使用卫语句,或者状态模式来实现。语句示例 如下:
public void today() {
if (isBusy()) { System.out.println(“change time.”); return; }
if (isFree()) { System.out.println(“go to travel.”); return; }
System.out.println(“stay at home to learn Alibaba Java Coding Guideline.”); return; }
【点评】规则好,严格遵循。
4. 【推荐】除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
说明:很多 if 语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么,如果阅读者分析逻辑表达式错误呢? 正例: //伪代码如下 final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) { ... }
反例: if ((file.open(fileName, "w") != null) && (...) || (...)) { ... }
【点评】规则好,严格遵循。
5. 【推荐】循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、 获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)。
【点评】规则好,严格遵循。
6. 【推荐】接口入参保护,这种场景常见的是用于做批量操作的接口。
7. 【参考】下列情形,需要进行参数校验:
1) 调用频次低的方法。
2) 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那得不偿失。
3) 需要极高稳定性和可用性的方法。
4) 对外提供的开放接口,不管是 RPC/API/HTTP 接口。
5) 敏感权限入口。
【点评】规则好,严格遵循。
8. 【参考】下列情形,不需要进行参数校验:
1) 极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查要求。
2) 底层调用频度比较高的方法。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底 层才会暴露问题。一般 DAO 层与 Service 层都在同一个应用中,部署在同一台服务器中,所 以 DAO 的参数校验,可以省略。
3) 被声明成 private 只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参 数已经做过检查或者肯定不会有问题,此时可以不校验参数
【点评】规则好,严格遵循。
(五) 注释规约
1. 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用 //xxx 方式。 说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注 释;在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高 阅读效率。
【点评】规则好,严格遵循。自己目前未严格遵守。
Javadoc 规范参考:http://www.2cto.com/kf/201607/521803.html
http://www.cnblogs.com/bluestorm/archive/2012/10/04/2711329.html
2. 【强制】所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。 说明:对子类的实现要求,或者调用注意事项,请一并说明。
【点评】规则好,严格遵循。
3. 【强制】所有的类都必须添加创建者和创建日期。
【点评】规则好,严格遵循。自己目前未严格遵守。
4. 【强制】方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释 使用/* */注释,注意与代码对齐。
【点评】规则好,严格遵循。自己目前未严格遵守。
5. 【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。
【点评】规则好,严格遵循。自己目前未严格遵守。
6. 【推荐】与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持 英文原文即可。 反例:“TCP 连接超时”解释成“传输控制协议连接超时”,理解反而费脑筋。
【点评】规则好,严格遵循。
7. 【推荐】代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑 等的修改。 说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后, 就失去了导航的意义。
【点评】规则好,严格遵循。
8. 【参考】合理处理注释掉的代码。在上方详细说明,而不是简单的注释掉。如果无用,则删除。 说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
【点评】规则好,严格遵循。
9. 【参考】对于注释的要求:第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含 义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同
天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。
【点评】规则好,严格遵循。
10. 【参考】好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的 一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。 反例: // put elephant into fridge put(elephant, fridge); 方法名 put,加上两个有意义的变量名 elephant 和 fridge,已经说明了这是在干什么,语 义清晰的代码不需要额外的注释。
【点评】规则好,严格遵循。
11. 【参考】特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描, 经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
1) 待办事宜(TODO):( 标记人,标记时间,[预计处理时间]) 表示需要实现,但目前还未实现的功能。这实际上是一个 Javadoc 的标签,目前的 Javadoc 还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 Javadoc 标签)。
2) 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间]) 在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况
【点评】规则好,严格遵循。