此文为阅读阿里巴巴Java开发手册时,将个人认为重要或值得注意的规范记作学习笔记。此为第一章——编程规约。
命名风格
各层命名规约(No.16)
- Service/DAO 层方法命名规约
- 获取单个对象的方法用 get 做前缀。
- 获取多个对象的方法用 list 做前缀,复数形式结尾如:listObjects。
- 获取统计值的方法用 count 做前缀。
- 插入的方法用 save/insert 做前缀。
- 删除的方法用 remove/delete 做前缀。
- 修改的方法用 update 做前缀。
- 领域模型命名规约
- 数据对象:xxxDO,xxx 即为数据表名。
- 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
- 展示对象:xxxVO,xxx 一般为网页名称。
- POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
常量定义
固定范围变量值使用Enum(No.5)
如果变量值仅在一个固定范围内变化用 enum
类型来定义。
说明:如果存在名称之外的延伸属性应使用 enum
类型,下面正例中的数字就是延伸信息,表示一年中的第几个季节。
public enum SeasonEnum {
SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);
private int seq;
SeasonEnum(int seq){
this.seq = seq;
}
}
代码格式
IDE代码格式(No.9)
IDE 的 text file encoding
设置为 UTF-8
; IDE 中文件的换行符使用 Unix
格式,
不要使用 Windows
格式。
OOP规约
包装类值比较(No.7)
所有的相同类型的包装类对象之间值的比较,全部使用 equals
方法比较。
说明:对于 Integer var = ? 在-128 至 127 范围内的赋值,Integer 对象是在IntegerCache.cache
产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals
方法进行判断。
基本数据类型与包装数据类型的使用标准(No.8)
- 【强制】所有的 POJO 类属性必须使用包装数据类型。
- 【强制】RPC 方法的返回值和参数必须使用包装数据类型。
- 【推荐】所有的局部变量使用基本数据类型。
说明:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE 问题,或者入库检查,都由使用者来保证。
正例:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。
反例:比如显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用
不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线。所以包装
数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。
String#spilt()(No.14)
使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛 IndexOutOfBoundsException
的风险。
说明:
String str = "a,b,c,,";
String[] ary = str.split(",");
// 预期大于 3,结果是 3
System.out.println(ary.length);
集合处理
Arrays.asList()(No.5)
使用工具类 Arrays.asList()
把数组转换成集合时,不能使用其修改集合相关的方
法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException
异常。
说明: asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。 Arrays.asList
体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[] { "you", "wu" };
List list = Arrays.asList(str);
第一种情况:list.add("yangguanbao");
运行时异常。
第二种情况:str[0] = "gujin";
那么 list.get(0)
也会随之修改。
集合初始化(No.10)
集合初始化时,指定集合初始值大小。
说明:HashMap 使用 HashMap(int initialCapacity)
初始化。
正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。
反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次被迫扩大,resize 需要重建 hash 表,严重影响性能。
entrySet 遍历 Map 类集合 KV(No.11)
使用 entrySet
遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。
说明: keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出key 所对应的 value。 而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,使用 Map.foreach
方法。
正例:values()返回的是 V 值集合,是一个 list 集合对象;keySet()返回的是 K 值集合,是
一个 Set 集合对象;entrySet()返回的是 K-V 值组合集合。
并发处理
多线程共享变量安全性(No.13)
volatile
解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,
但是如果多写,同样无法解决线程安全问题。
如果是 count++
操作,使用如下类实现:
AtomicInteger count = new AtomicInteger();
count.addAndGet(1);
如果是 JDK8,推荐使用 LongAdder
对象,比 AtomicLong
性能更好(减少乐观锁的重试次数)。
控制语句
高并发场景避免使用“等于”判断(No.3)
在高并发场景中,避免使用“等于”判断作为中断或退出的条件。
说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间
判断条件来代替。
反例:判断剩余奖品数量等于 0 时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变
成了负数,这样的话,活动无法终止。
参数校检(No.9)
- 调用频次低的方法。
- 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那得不偿失。
- 需要极高稳定性和可用性的方法。
- 对外提供的开放接口,不管是 RPC/API/HTTP 接口。
- 敏感权限入口。
其他
后台传输变量(No.3)
后台输送给页面的变量必须加$!{var}
——中间的感叹号。
说明:如果 var 等于 null 或者不存在,那么${var}会直接显示在页面上。
资料来源:
1.阿里巴巴Java开发手册(1.4.0)