Java开发规范
1.命名风格
- 参数名,变量名以小驼峰lowerCamelCase规范:
profileName
- 类名以大驼峰UpperCamelCase规范:
UserDO
- 常量名大写,单词下划线隔开
MAX_STOCK_COUNT
- 使用英文命名,不能出现拼音,以最为准确的翻译为准.
- 抽象类使用
Abstrac
t或者Base
开头,异常类使用Exception
结束
- 数组使用类型[] 变量名方式:
String[] names
,而不是String args[]
- 包名使用小写,不加下划线.
- 除了通用缩写方式,尽量使用完整单词组合命名.通用缩写见下表.
- 不能出现int a这样的"未知"变量名,必须使用一个可以表达含义的单词或短语.
- 接口中的方法和变量不加修饰符(方法默认public abstract,变量默认public static final)
- 当使用设计模式时,推荐类名使用设计模式名
OrderFactory,LoginProxy,DataObserver
- 业务类接口以I开头
IPresenter
,实现以ImplLoginPresenterImpl
结尾.
- 枚举类以
Enum
结尾.枚举成员大写加下划线分隔.
- 方法命名规范:
(1). 获取单个对象以get做前缀
(2). 获取多个对象以list做前缀,复数形式结尾: listBooks
(3). 获取统计值用count做前缀
(4). 插入方法以save/insert做前缀
(5). 删除方法以remove/delete做前缀
(6). 修改方法以update做前缀
- 领域对象命名规范:
(1). 数据库对象xxxDO
(2). 页面展示对象xxxVO
(3). 上传接口对象xxxReq
(4). 接口获取对象xxxRes
(5). 业务对象xxxBO
2.常量定义
- 在long或Long类型赋值时,数值使用大写L,而不是小写l,小写l容易和1混淆
- 常量类需要按功能分类,不能使用大而全的常量类.例:缓存使用CacheConsts,配置使用ConfigConsts
- 如果变量值仅在较小规模的固定范围内变化,使用enum,如季节,星期,生命周期,业务状态等.
3.代码格式
- 文件编码使用UTF-8,IDEA设置:
(1). Preference->Editor->Code Style-> File Encodings "Global Encoding","Project Encoding","Default encoding for properties files"都设置为"UTF-8"UTF-8"
(2). "Create UTF-8 files"设置为"with No BOM"
- 空格使用Unix格式,IDEA设置:
Preference->Editor->Code Style-> Line separator设置为"Unix and OS X (\n)
- 必须使用4个空格缩进,不能使用Tab,IDEA设置:
Preference->Editor->Code Style->Java -> Tabs and Indents -> "Tab size"和"Indent"设置为4,勿勾选"Use tab character"
- 每行超出120个字符换行,换行规则:
(1). 换行缩进4个空格,再换行不再缩进
(2). 运算符和下文一起换行
(3). 方法调用的.与方法一起换行
(4). 方法内多参数需换行时,在逗号后换行
IDEA设置:
(1). Preference->Editor->Code Style-> Hard wrap at 设置为120
(2). Preference->Editor->Code Style->Java ->Wrapping and Braces -> 勾选"Line breaks"和"Ensure right margin is not exceeded"
- 方法内部不同逻辑,不同语义,不同业务之间可以使用换行,代码任意位置不能出现多余1行的空行.IDEA设置:
Preference->Editor->Code Style->Java->Blank Lines->所有超过1的选项设置为1.
- 空格使用规范:
(1). if/for/while/switch/do 等保留字与括号之间都必须加空格。
(2). 小括号内左右第一个字符和小括号间不留空格
(3). 左大括号前加空格
(4). 二目三目运算符的左右两边都要加空格
(5). 双斜线和注释内容间仅使用一个空格
(6). 方法参数间使用一个空格
- 大括号使用规范:
(1). 左大括号前不换行
(2). 左大括号后换行
(3). 右大括号前有else等不换行
(4). 右大括号表示终止需换行
(5). 大括号内为空,可写成{},并不换行
- 推荐单个方法勿超过80行,超过80行考虑提取方法,保持单一责任原则.
4. OOP 规约
- 所有静态变量,常量,方法,都使用类名调用,勿使用对象调用.避免增加编译器解析成本.
- 所有覆写方法,需要加上@Override注解,避免方法名写错,编译时不报错.推荐使用快捷键调用"Override Members"覆写方法,避免出错.
- 避免使用Object作为参数,尽量不适用可变参数.
- 不能使用过时的类或方法.
- Object的equals方法容易抛空指针异常,应使用常量来调用equals:"cache".equals(xxx),或者使用Objects.equals(a,b)
- POJO类属性强制使用包装数据类型,如当数据库查询时结果可能为null,使用基本数据类型去接收会有NPE风险.并且基本数据类型的默认值可能会带来错误信息,比如当调用接口没有返回值时,默认的0,false等会带来歧义.null值表示出额外的信息:调用失败,服务器未返回该值等.
- 构造方法里不能加入业务逻辑,初始化逻辑应放在init方法中.
- POJO类必须重写toString()方法(Kotlin的data类默认已添加该方法,无需手动添加)
- 重载方法应按顺序放在一起,构造方法相同.在此基础上的方法置放顺序:public,proteced > private > getter/setter
- getter/setter方法应保持简单的get/set动作,不应插入业务逻辑.
- 循环体内,字符串的连接使用StringBuilder的append方法,使用字符串直接+连接会造成每次循环new出StringBuilder对象.(Kotlin可使用"${var1}var2"方式连接)
- 方法,属性权限应给满足使用的最小权限.
(1). 类内使用:private
(2). 包内和子类使用:protected.
过于宽泛的权限会导致不当调用泛滥,不利于解耦.(一旦在其他地方被调用,这个方法,属性就不止属于类自己了)
- 不能再foreach循环里进行元素的remove/add操作.可能会引起运行时异常.
- 为了降低记忆负担,Map中不要使用null作为key,只有HashMap和TreeMap可以存储null的value.
- 合理利用好集合的有序性(sort)和稳定性(order),避免集合的无序性(unsort)和
不稳定性(unorder)带来的负面影响。
有序性是指遍历的结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次
序是一定的。如:ArrayList 是 order/unsort;HashMap 是 unorder/unsort;TreeSet 是order/sort。
- 可以用用Set的唯一性快速去重,不要使用List的contains方法进行遍历对比去重.
- 表达异常的分支,少用if-else,减少if-else嵌套,嵌套不能超过3层,消灭箭头语句,尽量使用卫语句(剪枝逻辑):
No:
if(condition1){
} esle if(condition2) {
} else if(condition3) {
}
Yes:
if(condition1){
}
if(condition2){
}
if(condition3){
}
- 不要直接使用复杂的判断逻辑,将复杂的判断表达式赋值给一个有意义的布尔变量,调代码可读性.
正例:
// 伪代码如下
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {
...
}
反例:
if ((file.open(fileName, "w") != null) && (...) || (...)) {
...
}
- 校验参数规范:
(1). 执行流程较为复杂的方法需要进行参数校验(FastFail)
(2). 与用户输入相关方法需校验参数.
(3). 对外提供的开放接口需校验参数.
(4). 较为底层且调用次数频繁方法不需要校验参数.
5. 注释规约
- 类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用
// xxx 方式。
说明:在 IDE 编辑窗口中,Javadoc方式会提示相关注释,生成Javadoc可以正确输出相应注释;在IDE中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
- 所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
说明:对子类的实现要求,或者调用注意事项,需一并说明。
- 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释
使用/* */注释,注意与代码对齐。
- 注释掉的代码用注释说明注释原因,注释原因用///注释,如果不再使用,删除该代码,使用版本控制追溯.
- 对于注释的要求:
(1). 能够准确反应设计思想和代码逻辑
(2). 能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。
(3). 保持和代码的同步.
(4). 尽量使用常用,简单的英语进行注释.可搭配翻译使用,避免中式英语.
(5). 使用最简练的语言写注释,好的命名、代码结构是自解释的,避免过多过滥的注释.
反例:
// put elephant into fridge
put(elephant, fridge);
方法名 put,加上两个有意义的变量名 elephant 和 fridge,已经说明了这是在干什么,语
义清晰的代码不需要额外的注释。
- 特殊注释标记
(1). 待办事宜(TODO):( 标记人,标记时间,[预计处理时间])
(2). 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])
6. 异常处理规约
- 异常因使用最小粒度进行捕获,不能使用一个exception进行捕获.注意多个异常排列的顺序.
- 捕获异常应尽量catch最小的单元,不要catch过多内容.
- 捕获异常之后必须处理.推荐异常进行日志记录上报(Do not swallow)
- 资源需要在finally中释放,推荐使用try-with-resource的方式.
- 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。使用 JDK8 的 Optional 类来防止 NPE 问题。(Kotlin可使用?.调用方式处理)
if(obj == null){
return;
}
if(a == null){
return;
}
if(b == null){
return;
}
b.getC();
if(obj?.getA()?.getB()?.getC() == null){
return
}
obj!!.getA();
7. 日志规约
- 日志文件至少保存15天,有些异常是以"周"为频次发生的.
- 对 trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方
式。
反例
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
正例:(条件)建设采用如下方式
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
}
正例:(占位符)
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
Kotlin中可采取
logger.debug("Processing trade with id: ${id} and symbol: ${symbol}");
- 日志不可重复打印,冗余打印,保持精简.
- 异常日志应记录必要的现场信息和堆栈信息
正例:
logger.error(各类参数或者对象 toString() + "_" + e.getMessage(), e);
- 生产环境应关闭debug日志.(远程调试除外)
讨论
- 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
- 每个switch需要带上default,case语句如果没有break语句,需要在末尾注释// fall down
-
TODO:单元测试规约
参考:
阿里巴巴java开发手册
插件使用文档
阿里巴巴Android开发手册