代码规范
【基础】
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 | public void demo(int a, int b, int c) { |
减少嵌套的方法
- 合并条件
- 利用 return 以省略后面的 else
- 利用子方法
1 | public void demo(int a, int b, int c) { |
接口类中的方法和属性不要加任何修饰符号
接口类中的方法和属性不要加任何修饰符号(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 | 正例:"test".equals(object); |
所有的相同类型的包装类对象之间值的比较,全部使用 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 | List<String> list = new ArrayList<String>(2); |
使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方
法
asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList
体现的是适配器模式,只是转换接口,后台的数据仍是数组。
1 | String[] str = new String[] { "you", "wu" }; |
不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator
方式,如果并发操作,需要对 Iterator 对象加锁
1 | Iterator<String> iterator = list.iterator(); |
集合初始化时,指定集合初始值大小
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 | if (condition) { |
循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、
获取数据库连接,进行不必要的 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 个之内。