梦想还是要有的,万一忘了咋办?

0%

Java开发规约

目录:

  • 规约
  • 参考

1、命名规约

1.1、【强制】禁止使用非标准的英文缩写

1
反例: AbstractClass 缩写成 AbsClass;condition 缩写成 condi。

1.2、 【强制】命名禁止出现 “_”

1
反例: _name 、 name_ 、i_name

1.3、 【推荐】包名全部小写。点分隔符之间尽量只有一个英语单词,即使有多个单词也不使用下划线或大小写分隔

1
2
3
正例: com.vip.javatool

反例: com.vip.java_tool, com.vip.javaTool

1.4、 【强制】类名与接口名使用UpperCamelCase风格,遵从驼峰形式
Tcp, Xml等缩写也遵循驼峰形式,可约定例外如:DTO/ VO等

1
2
3
正例:UserId / XmlService / TcpUdpDeal / UserVO

反例:UserID / XMLService / TCPUDPDeal / UserVo

1.5、 【强制】方法名、参数名、成员变量、局部变量使用lowerCamelCase风格,遵从驼峰形式

1
正例: localValue / getHttpMessage();

1.6. 【强制】常量命名全大写,单词间用下划线隔开。力求语义表达完整清楚,不要嫌名字长

1
2
3
正例: MAX_STOCK_COUNT

反例: MAX_COUNT

例外:当一个static final字段不是一个真正常量,比如不是基本类型时,不需要使用大写命名。

1
private static final Logger logger = Logger.getLogger(MyClass.class);

1.7. 【推荐】枚举类名以Enum结尾; 抽象类使用Abstract或Base开头;异常类使用Exception结尾;测试类以它要测试的类名开始,以Test结尾

1
正例:DealStatusEnum, AbstractView,BaseView, TimeoutException,UserServiceTest

1.8. 【推荐】实现类尽量用Impl的后缀与接口关联,除了形容能力的接口

1
2
3
正例:CacheServiceImpl 实现 CacheService接口。

正例: Foo 实现 Translatable接口。

1.9. 【强制】POJO类中布尔类型的变量名,不要加is前缀,否则部分框架解析会引起序列化错误

1
反例:Boolean isSuccess的成员变量,它的GET方法也是isSuccess(),部分框架在反射解析的时候,“以为”对应的成员变量名称是success,导致出错。

1.10、各层命名规约

A) Service/DAO 层方法命名规约
1) 获取单个对象的方法用 get 做前缀。
2) 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects。
3) 获取统计值的方法用 count 做前缀。
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。

2、格式规约

2.1、【强制】使用项目组统一的代码格式模板,基于IDE自动的格式化
2.2、【强制】IDE的text file encoding设置为UTF-8; IDE中文件的换行符使用Unix格式,不要使用Windows格式
2.3、【推荐】 用小括号来限定运算优先级
2.4、【推荐】 类内方法定义的顺序,不要“总是在类的最后添加新方法”
一个类就是一篇文章,想象一个阅读者的存在,合理安排方法的布局。

1)顺序依次是:构造函数 > (公有方法>保护方法>私有方法) > getter/setter方法。

如果公有方法可以分成几组,私有方法也紧跟公有方法的分组。

2)当一个类有多个构造方法,或者多个同名的重载方法,这些方法应该放置在一起。其中参数较多的方法在后面。

1
2
3
4
5
public Foo(int a) {...}
public Foo(int a, String b) {...}

public void foo(int a) {...}
public void foo(int a, String b) {...}

3)作为调用者的方法,尽量放在被调用的方法前面。

1
2
3
4
5
public void foo() {
bar();
}

public void bar() {...}

2.5、【推荐】通过空行进行逻辑分段
一段代码也是一段文章,需要合理的分段而不是一口气读到尾。

不同组的变量之间,不同业务逻辑的代码行之间,插入一个空行,起逻辑分段的作用。

而联系紧密的变量之间、语句之间,则尽量不要插入空行。

1
2
3
4
int width;
int height;

String name;

2.6、【推荐】避免IDE格式化
对于一些特殊场景(如使用大量的字符串拼接成一段文字,或者想把大量的枚举值排成一列),为了避免IDE自动格式化,土办法是把注释符号//加在每一行的末尾,但这有视觉的干扰,可以使用@formatter:off和@formatter:on来包装这段代码,让IDE跳过它。

1
2
3
// @formatter:off
...
// @formatter:on

3、注释规约

3.1、【推荐】基本的注释要求

完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。

代码将被大量后续维护,注释如果对阅读者有帮助,不要吝啬在注释上花费的时间。(但也综合参见规则2,3)

第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。

除了特别清晰的类,都尽量编写类级别注释,说明类的目的和使用方法。

除了特别清晰的方法,对外提供的公有方法,抽象类的方法,同样尽量清晰的描述:期待的输入,对应的输出,错误的处理和返回码,以及可能抛出的异常。

3.2、【推荐】通过更清晰的代码来避免注释

在编写注释前,考虑是否可以通过更好的命名,更清晰的代码结构,更好的函数和变量的抽取,让代码不言自明,此时不需要额外的注释。

3.3、【推荐】避免创建人,创建日期,及更新日志的注释

代码后续还会有多人多次维护,而创建人可能会离职,让我们相信源码版本控制系统对更新记录能做得更好。

3.4、【强制】代码修改的同时,注释也要进行相应的修改。尤其是参数、返回值、异常、核心逻辑等的修改

3.5、【强制】类、类的公有成员、方法的注释必须使用Javadoc规范,使用/** xxx */格式,不得使用//xxx方式

3.6、【推荐】注释不要为了英文而英文

如果没有国际化要求,中文能表达得更清晰时还是用中文。

3.7、【推荐】TODO 标记,清晰说明代办事项和处理人

正例:

1
2
3
// TODO(who): to do what.
// TODO(who): when to do what.
// TODO(calvin): use xxx to replace yyy.

反例:

1
// TODO: refactor it

不推荐使用 FIXME 标记。这样,在项目中搜索 TODO 就可以得到完整的 TODO list,而不需要搜索 TODO 和 FIXME。

3.8、【推荐】合理处理注释掉的代码

如果后续会恢复此段代码,在目标代码上方用///说明注释动机,而不是简单的注释掉代码。

如果很大概率不再使用,则直接删除(版本管理工具保存了历史代码)。

4、方法设计

4.1、【推荐】方法的长度度量

方法尽量不要超过100行,或其他团队共同商定的行数。
另外,方法长度超过8000个字节码时,将不会被JIT编译成二进制码。

4.2、

5、类设计

5.1、【强制】除了保持兼容性的情况,总是移除无用属性、方法与参数

特别是private的属性、方法、内部类,private方法上的参数,一旦无用立刻移除。信任代码版本管理系统。

6、控制语句

6.1、【推荐】少用if-else方式,多用哨兵语句式以减少嵌套层次

1
2
3
4
5
6
7
8
9
10
11

if (condition) {
...
return obj;
}
if (condition2) {
...
return obj2;
}

// 接着写else的业务逻辑代码;

Facebook-Contrib: Style - Method buries logic to the right (indented) more than it needs to be

6.2、【强制】switch的规则
1)在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;

2)在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String animal = "tomcat";

switch (animal) {
case "cat":
System.out.println("It's a cat.");
break;
case "lion": // 执行到tiger
case "tiger":
System.out.println("It's a beast.");
break;
default:
// 什么都不做,也要有default
break;
}

7、基本类型与字符串

7.1、 数值equals比较的原则

  1. 【强制】 所有包装类对象之间值的比较,全部使用equals方法比较
  2. 【强制】 BigDecimal需要使用compareTo()
  3. 【强制】 Atomic* 系列,不能使用 equals 方法
    因为 Atomic* 系列没有覆写 equals 方法。
1
2
3
4
// RIGHT
if (counter1.get() == counter2.get()) {
// ...
}
  1. 【强制】 double 及 float 的比较,要特殊处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

float f1 = 0.15f;
float f2 = 0.45f/3; // 实际等于0.14999999

// 反例
if (f1 == f2) {
// ...
}

// 反例
if (Double.compare(f1, f2) == 0) {
// ...
}

// 正例
// 绝对误差
static final float EPSILON = 0.00001f;
if (Math.abs(f1 - f2) < EPSILON) {
// ...
}

// 正例
// 相对误差
static final float EPSILON = 0.001f;
if (Math.abs(f1 - f2) / (Math.abs(f1) + Math.abs(f2)) < EPSILON) {
// ...
}

推荐使用“相对误差”的方式比较浮点数。

7.2、【推荐】 double 或 float 计算时有不可避免的精度问题

1
2
3
4
5
float f = 0.45f/3;    //结果是0.14999999

double d1 = 0.45d/3; //结果是正确的0.15

double d2 = 1.03d - 0.42d; //结果是0.6100000000000001

尽量用double而不用float,但如果是金融货币的计算,则必须使用如下选择:

选项1, 使用性能较差的BigDecimal。BigDecimal还能精确控制四舍五入或是其他取舍的方式。

选项2, 在预知小数精度的情况下,将浮点运算放大为整数计数,比如货币以"分"而不是以"元"计算。

8、集合处理

8.1、【强制】不要在foreach循环里进行元素的remove/add操作,remove元素可使用Iterator方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//WRONG
for (String str : list) {
if (condition) {
list.remove(str);
}
}

//RIGHT
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String str = it.next();
if (condition) {
it.remove();
}
}

8.2、【强制】使用entrySet遍历Map类集合Key/Value,而不是keySet 方式进行遍历

keySet遍历的方式,增加了N次用key获取value的查询。


参考:
《唯品会Java开发手册》1.0.3版