【注明】本文重点参考《阿里java开发规范》并总结从业六年来所在几家公司对项目规范的要求。对阿里巴巴技术部各位前辈、老师表示感谢!
1 客户端架构设计规范
项目架构是大智慧,用来对软件设计进行分工;设计模式是小技巧,对具体问题提出解决方案,一切以提高代码复用率,降低耦合度。
一个好的架构关键因素是代码/包的组织:程序员浏览源代码遇到的第一件事情就是包结构。一切从它流出,一切依赖于它。
1.1 设计原则:高内聚性,模块化,低偶合
用包来体现特性集。把所有相关某一特性(且仅特性相关)的项放入一个包中。这样包的内聚性高,模块化程度高,包之间偶合度低。紧密相关的项放在一起,没有分散到整个应用程序中。代码的所有权会更容易组织,也更容易被模块化,高复用性。
2 命名规范
所有编程相关命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束!
所有编程相关命名严禁使用拼音与英文混合的方式!
所有编程相关命名严禁不规范的英文缩写!
2.1 包名
2.1.1 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式。
2.1.2 包名首前缀:
com:公司项目
team:团队项目
indi:个体项目,指个人发起,但非自己独自完成的项目,可公开或私有项目
pers:个人项目,指个人发起,独自完成,可分享的项目
priv:私有项目,指个人发起,独自完成,非公开的私人使用的项目
2.1.3 包名命名顺序:
com.公司名.项目名.模块名....
team.工作室名/团队名.项目名.模块名....
indi.个人名.项目名.模块名....
pers.个人名.项目名.模块名....
priv.个人名.项目名.模块名....
2.2 类名:
2.2.1 类名使用UpperCamelCase风格,必须遵从驼峰形式,但以下情形例外:(领域模型的相关命名)DO / DTO / VO / DAO等。
2.2.2 如果使用到了设计模式,建议在类名中体现出具体模式。将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。
例如:
publicclass OrderFactory; public class LoginProxy; public class ResourceObserver;
2.2.3 枚举类名建议带上Enum后缀,枚举成员名称需要全大写。
2.2.4 抽象类命名使用Abstract或Base开头;异常类命名使用Exception结尾;测试类命名以它要测试的类的名称开始,以Test结尾。
2.2.5 对于Service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别。例如:CacheServiceImpl实现CacheService接口。
2.3 方法名:
2.3.1 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格,必须遵从驼峰形式。
2.3.2 接口类中的方法和属性不要加任何修饰符号(public也不要加)。
2.4 变量名:
2.4.1 数组类变量名:中括号是数组类型的一部分,数组定义如下:String[] args;反例:String args[]
2.4.2 POJO类中的任何布尔类型的变量,都不要加is,否则部分框架解析会引起序列化错误。
2.5 常量名:
2.5.1 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚
2.5.2 不允许出现任何未经定义的常量直接出现在代码中String key="Id#taobao_"+tradeId;
2.5.3 long或者Long初始赋值时,必须使用大写的L,不能是小写的l,小写容易跟数字1混淆,造成误解。
2.5.4 不推荐使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量,应分开定义维护。
3 注释规范
3.1 要求全英文注释。
3.2 类、类属性、类方法注释
要求必须使用javadoc规范,使用/**内容*/格式,内容包括方法、参数、返回值的意义,高效率阅读。
例如:
/**
*request by JSON style
*
*@paramrequest
*/
3.3 类注释:
所有的类,必须添加作者信息,时间信息,描述信息
例如:
/**
*文件工具类
*
*@authorsky_dreaming on 2016/08/08
*
*/
3.4 方法内部代码单行注释:
在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
例如:
/* about cookie save cookies at local */
if(null!=request.mContext&&AsyncHttpUtil.isNeedCookie()) {
CookieUtils.setCookies(CookieUtils.getCookie(request.mContext));
}
3.5 代码修改注释:
版本迭代,后期维护过程中修改原有代码,要求在修改代码前后另起一行注释,说明修改作者,日期,需求,注意事项等等。
例如:
// modified by qiang.hou on 2016/07/03 forsend response message to UI thread begins
…………………………………………
// modified by qiang.hou on 2016/07/03 for
send response message to UI thread ends
3.6 代码添加注释:
版本迭代,后期维护过程中增加代码,要求在增加代码前后另起一行注释,说明修改作者,日期,需求,注意事项等等。
// added by qiang.hou on 2016/07/03 forsend response message to UI thread begins
…………………………………………
// added by qiang.hou on 2016/07/03 for
send response message to UI thread ends
3.7 注释同步更新:代码更新的同时,同步注释更新。
说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。
3.8 枚举类型注释:
必须说明每个字段的用途。
3.9 注释掉的代码块:
起始位置向上另起一行注释,要求说明该代码块原功能,注释作者,注释时间,注释动机等等,以及后期是否恢复使用。
3.10 标准注释要求:
第一、注释要求能够准确反应设计思想和代码逻辑;
第二、注释要求能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。
第三、注释要求精简准确、表达到位,过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
第四、要求代码中类,方法,变量,常量等命名规则尽量做到自然注释。例如:getSystemInfo
4 集合框架使用规范
4.1 Map/Set的key为自定义对象时,必须重写hashCode和equals。(String除外,因为本身已经进行了重写)
4.2 ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ;说明:subList返回的是ArrayList的内部类SubList,并不是ArrayList,而是ArrayList的一个视图,对于SubList子列表的所有操作最终会反映到原列表上。
4.3 使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。说明:asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。String[] str = new
String[] { "a", "b" }; List list = Arrays.asList(str);第一种情况:list.add("c");运行时异常。第二种情况:str[0]= "gujin";那么list.get(0)也会随之修改。
4.4 不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
4.5 线程安全:
线程安全是指任何时刻都只有一个线程访问临界资源。线程安全并不是说他的一系列操作是同步的只是对于他执行某个方法的时候不允许别的线程去改变。针对一个类来说是不是线程安全就要看,多个线程在同时在运行,这些线程可能会同时执行某个方法。但是每次运行结果和单线程执行的结果一样,那么就可以说是线程安全的。
通常涉及系统安全的变量一般都是成员变量
1)vector:就比arraylist多了个同步化机制(线程安全)
2)hashtable:就比hashmap多了个线程安全(线程安全)
3)statck:堆栈类,先进后出(线程安全)
4)enumeration:枚举,相当于迭代器(线程安全)
5)StringBuffer:是线程安全,而StringBuilder是线程不安全的。stringBuffer本身的内部实现是现场安全的!线程安全那是类本身提供的功能是安全的。即你提供插入一个字符串,那么这个字符串插入是安全的。
5 OOP规范
5.1 方法可变参数使用:
相同参数类型,相同业务含义,才可以使用Java的可变参数,避免使用Object。可变参数必须放置在参数列表的最后。
5.2 不能使用过时的类或方法。
5.3 equals方法:
所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。
5.4 构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在create或者init方法中。
5.5 静态变量/方法调用:
需要访问类的静态变量和静态方法时候,必须直接使用类名直接访问,否则无谓增加编译器解析成本。
5.6 空指针预判:
在任何不百分百确定对象有值的情况下,在调用对象的属性或者方法之前,必须添加空指针预判代码。
5.7 final声明:
final可提高程序响应效率。以下情况下推荐使用final声明。
1)不需要重新赋值的变量,包括类属性、局部变量。
2)对象参数前加final,表示不允许修改引用的指向。
3)类方法确定不允许被重写。
5.8 类成员与方法访问控制:
1)如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
2)工具类不允许有public或default构造方法。
3)类非static成员变量并且与子类共享,必须是protected。
4)类非static成员变量并且仅在本类使用,必须是private。
5)类static成员变量如果仅在本类使用,必须是private。
6)若是static成员变量,必须考虑是否为final。
7)类成员方法只供类内部调用,必须是private。
8)类成员方法只对继承类公开,那么限制为protected。
说明:任何类、方法、参数、变量,严控访问范围。过宽泛的访问范围,不利于模块解耦。思考:如果是一个private的方法,想删除就删除,可是一个public的Service方法,或者一个public的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,如果无限制的到处跑,那么你会担心的。
5.9 慎用对象的clone方法:
方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝。
5.10 String和StringBuffer:
对象需要循环叠加的,循环体内,字符串的联接方式,使用StringBuilder的append方法进行扩展。
5.11 类内方法定义顺序:
推荐:公有方法或保护方法>私有方法> getter/setter方法。
5.12 强制重写JavaBean类的toString()方法
5.13 接口签名:
对外暴露的接口签名,原则上不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated注解,并清晰地说明采用的新接口或者新服务是什么。
5.14 接口类方法定义:
接口类中的方法和属性不要加任何修饰符号(public也不要加),保持代码的简洁性,并加上有效的javadoc注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。