代码规范

【基础】
1,变量/类/文件命名要规范(大小写,含义)
2,注释适度
3,代码整形
4,禁止相同处理重复copy

【提高】
1,考虑空指针场景
2,考虑数据类型精度
3,考虑事务的回滚(后端)
4,考虑异常时的处理机制

【性能】
1,减少无效循环次数
2,减少数据库访问次数(后端)

方法名

前缀名 意义 举例
create 创建 createOrder()
delete 删除 deleteOrder()
add 创建,暗示新创建的对象属于某个集合 addPaidOrder()
remove 删除 removeOrder()
init或则initialize 初始化,暗示会做些诸如获取资源等特殊动作 initializeObjectPool()
destroy 销毁,暗示会做些诸如释放资源的特殊动作 destroyObjectPool()
open 打开 openConnection()
close 关闭 closeConnection()
read 读取 readUserName()
write 写入 writeUserName()
get 获得 getName()
set 设置 setName()
prepare 准备 prepareOrderList()
copy 复制 copyCustomerList()
modity 修改 modifyActualTotalAmount()
calculate 数值计算 calculateCommission()
do 执行某个过程或流程 doOrderCancelJob()
dispatch 判断程序流程转向 dispatchUserRequest()
start 开始 startOrderProcessing()
stop 结束 stopOrderProcessing()
send 发送某个消息或事件 sendOrderPaidMessage()
receive 接受消息或时间 receiveOrderPaidMessgae()
respond 响应用户动作 responseOrderListItemClicked()
find 查找对象 findNewSupplier()
update 更新对象 updateCommission()

减少代码嵌套层次

代码嵌套层次达 3 层以上时,一般人理解起来都会困难

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void demo(int a, int b, int c) {
if (a > b) {
if (b > c) {
doJobA();
} else if (b < c) {
doJobB()
} else {
if (b > c) {
if (a < c) {
doJobC();
}
}
}
}
}

减少嵌套的方法

  1. 合并条件
  2. 利用 return 以省略后面的 else
  3. 利用子方法
1
2
3
4
5
6
7
8
9
10
11
public void demo(int a, int b, int c) {
if (a > b && b > c) {
doJobA();
}
if (a > b && c > b) {
doJobB();
}
if (a <= b && c < b && a < c) {
doJobC();
}
}

接口类中的方法和属性不要加任何修饰符号

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

各层命名规约

Service/DAO 层方法命名规约

1) 获取单个对象的方法用 get 做前缀。

2) 获取多个对象的方法用 list 做前缀。

3) 获取统计值的方法用 count 做前缀。

4) 插入的方法用 save/insert 做前缀。

5) 删除的方法用 remove/delete 做前缀。

6) 修改的方法用 update 做前缀。

领域模型命名规约

1) 数据对象:xxxDO,xxx 即为数据表名。

2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。

3) 展示对象:xxxVO,xxx 一般为网页名称。

4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。

常量定义

不允许任何魔法值(即未经定义的常量)直接出现在代码中

1
反例:String key = "Id#taobao_" + tradeId;

long 或者 Long 初始赋值时,使用大写的 L,不能是小写的 l,小写容易跟数字 1 混
淆,造成误解。

大括号的使用约定

如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果
是非空代码块则

1.左大括号前不换行。

2.左大括号后换行。

3.右大括号前换行。

4.右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。

OOP 规约

避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成
本,直接用类名来访问即可

相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object。

1
正例:public User getUsers(String type, Integer... ids) {...}

外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生
影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。

Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用
equals。

1
2
正例:"test".equals(object);
反例:object.equals("test");

所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。

对于 Integer var = ? 在-128 至 127 范围内的赋值,Integer 对象是在
IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行
判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,
推荐使用 equals 方法进行判断。

关于基本数据类型与包装数据类型的使用标准

1) 【强制】所有的 POJO 类属性必须使用包装数据类型。

2) 【强制】RPC 方法的返回值和参数必须使用包装数据类型。

3) 【推荐】所有的局部变量使用基本数据类型。

集合处理

关于 hashCode 和 equals 的处理

1) 只要重写 equals,就必须重写 hashCode。

2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的
对象必须重写这两个方法。

3) 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals。

使用集合转数组的方法,必须使用集合的 toArray(T[] array)

使用 toArray 带参方法,入参分配的数组空间不够大时,toArray 方法内部将重新分配
内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[ list.size() ]的数组
元素将被置为 null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素
个数一致。

1
2
3
4
5
List<String> list = new ArrayList<String>(2); 
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);

使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方

asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList
体现的是适配器模式,只是转换接口,后台的数据仍是数组。

1
2
3
4
String[] str = new String[] { "you", "wu" };
List list = Arrays.asList(str);
第一种情况:list.add("yangguanbao"); 运行时异常。
第二种情况:str[0] = "gujin"; 那么 list.get(0)也会随之修改。

不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator
方式,如果并发操作,需要对 Iterator 对象加锁

1
2
3
4
5
6
7
Iterator<String> iterator = list.iterator(); 
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
}

集合初始化时,指定集合初始值大小

HashMap 使用 HashMap(int initialCapacity) 初始化

initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader
factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)

使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历

keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出
key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效
率更高。如果是 JDK8,使用 Map.foreach 方法。

利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的
contains 方法进行遍历、对比、去重操作

控制语句

在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程
序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且
放在最后,即使它什么代码也没有

在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码,避免采用
单行的编码方式:if (condition) statements;

表达异常的分支时,少用 if-else 方式

1
2
3
4
5
if (condition) { 
...
return obj;
}
// 接着写 else 的业务逻辑代码;

循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、
获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)

下列情形,需要进行参数校验

1) 调用频次低的方法。

2) 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参
数错误导致中间执行回退,或者错误,那得不偿失。

3) 需要极高稳定性和可用性的方法。

4) 对外提供的开放接口,不管是 RPC/API/HTTP 接口。

5) 敏感权限入口。

SQL 语句

不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的
标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关

count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行

当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为
NULL,因此使用 sum()时需注意 NPE 问题

使用 ISNULL()来判断是否为 NULL 值

不得使用外键与级联,一切外键概念必须在应用层解决

禁止使用存储过程,存储过程难以调试和扩展,更没有移植性

in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控
制在 1000 个之内。