0%

阿里巴巴Java开发手册

读阿里巴巴 java 开发手册的收获

编程规约

命名规约

  1. 类名使用 UpperCamelCase风格,必须遵从驼峰形式

    ![image-20201214111207101](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201214111207101.png)

  2. 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格,必须遵从驼峰形式

    ![image-20201214111947213](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201214111947213.png)

  3. 各层命名规约

    A) Service/DAO 层方法命名规约(重要)

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

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

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

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

    5) 删除的方法用 delete做前缀(我们采用del或者delete)。

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

    B) 领域模型命名规约

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

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

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

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

常量定义

  1. long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟数字 1

    混淆,造成误解。

    Long a = 2l; 写的是数字的 21,还是 Long 型的 2?

  2. 如果变量值仅在一个范围内变化用 Enum 类。如果还带有名称之外的延伸属性,必须

    使用 Enum 类。

    ![image-20201214113040962](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201214113040962.png)

格式规约

  1. 大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果

    是非空代码块则:

    1) 左大括号前不换行。

    2) 左大括号后换行。

    3) 右大括号前换行。

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

  2. 左括号和后一个字符之间不出现空格;同样,右括号和前一个字符之间也不出现空

    格。

  3. if/for/while/switch/do 等保留字与左右括号之间都必须加空格。

  4. 任何运算符左右必须加一个空格。

![image-20201214113853298](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201214113853298.png)

  1. 单行字符数限制不超过 120 个,超出需要换行,遵循如下原则:

    1) 换行时相对上一行缩进 4 个空格。

    2) 运算符与下文一起换行。

    3) 方法调用的点符号与下文一起换行。

    4) 在多个参数超长,逗号后进行换行。

    5) 在括号前不要换行。

    ![image-20201214114141972](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201214114141972.png)

  2. 方法参数在定义和传入时,多个参数逗号后边必须加空格。

    1
    method("a", "b", "c");
  3. IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,不

    要使用 windows 格式。

OOP规约

  1. 所有的覆写方法,必须加@Override 注解。例如:getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加@Override可以准确判断是否覆盖成功。

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

  3. 关于基本数据类型与包装数据类型的使用标准如下:

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

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

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

    (数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险)(NPE:为基本类型的数据返回null值

集合处理

  1. 使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一样的数组,大小就是 list.size(),例如:
1
2
3
4
5
6
7
8
9
List<String> list = new ArrayList<String>(2);

list.add("guan");

list.add("bao");

String[] array = new String[list.size()];

array = list.toArray(array);
  1. 使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。

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

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

1
2
3
4
5
6
7
8
9
Iterator<String> it = a.iterator();

while(it.hasNext()) {
String temp =
it.next();
if(删除元素的条件) {
it.remove();
}
}
  1. ,Comparator 要满足自反性,传递性,对称性,不然 Arrays.sort,Collections.sort 会报IllegalArgumentException 异常。

    1) 自反性:x,y 的比较结果和 y,x 的比较结果相反。

    2) 传递性:x>y,y>z,则 x>z。

    3) 对称性:x=y,则 x,z 比较结果和 y,z 比较结果相同。

  2. 使用 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 值组合集合。

    ![image-20201214143611543](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201214143611543.png)

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

并发处理

  1. 不要显式创建进程,使用线程池

![image-20201215091104537](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201215091104537.png)

使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

  1. 控制语句

  2. 在 if/else/for/while/do 语句中必须使用大括号,即使只有一行代码,避免使用下面的形式:if (condition) statements;

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

  4. 关于参数校验

    • 需要参数校验的场景

      1) 调用频次低的方法。

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

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

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

    • 不需要参数校验的场景

      1) 极有可能被循环调用的方法,不建议对参数进行校验。但在方法说明里必须注明外部参数检查。

      2) 底层的方法调用频度都比较高,一般不校验。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底层才会暴露问题。一般 DAO 层与 Service 层都在同一个应用中,部署在同一台服务器中,所以 DAO 的参数验,可以省略。

      3) 被声明成 private 只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参数已经做过检查或者肯定不会有问题,此时可以不校验参数。

  5. 除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行复杂的语句,以提高可读性。

注释规约

  1. 类、类属性、类方法的注释必须使用 javadoc 规范,使用/*内容/格式,不得使用//xxx 方式。说明:在 IDE 编辑窗口中,javadoc 方式会提示相关注释,生成 javadoc 可以正确输出相应注释;在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。类似下面这种:

![image-20201215092118171](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201215092118171.png)

  1. 所有的类都必须添加创建者信息。

  2. 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。

  3. 所有的枚举类型字段必须要有注释,说明每个数据项的用途。

  4. 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。(说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义)

  5. 特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。

    1) 待办事宜(TODO):( 标记人,标记时间,[预计处理时间])

    ​ 表示需要实现,但目前还未实现的功能。这实际上是一个 javadoc 的标签,目前的javadoc 还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 javadoc标签)

    2)错误,不能工作(FIXME):(标记人,标记时间,[预处理时间])

    ​ 在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。

其他

  1. 注意 Math.random() 这个方法返回是 double 类型,注意取值范围 0≤x<1(能够取到零值,注意除零异常),如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。如:
1
2
3
Random random = new Random();
//产生0-99的随机数
int x = random.nextInt(100);
  1. 获取当前毫秒数:System.currentTimeMillis(); 而不是 new Date().getTime();

    说明:如果想获取更加精确的纳秒级时间值,用 System.nanoTime。在 JDK8 中,针对统计时间等场景,推荐使用 Instant 类。

    1
    2
    3
    4
    Instant instant = Instant.now();//当前瞬时时间
    System.out.println(instant.getEpochSecond());//当前瞬间秒数
    System.out.println(instant.toEpochMill());//当前瞬间毫秒数
    System.out.println(instant.getNano);//当前瞬间纳秒数

异常日志

异常处理

  1. 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。

  2. 防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:

    1) 返回类型为包装数据类型,有可能是 null,返回 int 值时注意判空。

    2) 数据库的查询结果可能为 null。

    3) 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。

    4) 远程调用返回对象,一律要求进行 NPE 判断。

    5) 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。

    6) 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。

  3. 在代码中使用“抛异常”还是“返回错误码”,对于公司外的 http/api 开放接口必须使用“错误码”;而应用内部推荐异常抛出;跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess、“错误码”、“错误简短信息”。

![image-20201215114506068](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201215114506068.png)

![image-20201215114643149](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201215114643149.png)

日志规约

  1. 应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

![image-20201215114927126](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201215114927126.png)

  1. 避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false

![image-20201215115308588](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201215115308588.png)

MYSQL规约

建表

  1. 表名、字段名必须使用小写字母或数字;禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
  2. 表名不使用复数名称

![image-20201215115943248](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201215115943248.png)

  1. 小数类型为 decimal,禁止使用 float 和 double。

    说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。

  2. 表必备三字段:id, gmt_create, gmt_modified。

    说明::其中 id 必为主键,类型为 unsigned bigint、单表时自增、步长为 1;gmt_create, gmt_modified 的类型均为date_time 类型。

    ![image-20201215131606546](/Users/heqinyang/Library/Application Support/typora-user-images/image-20201215131606546.png)

    1. 合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。

      例如:人的年龄用 unsigned tinyint(表示范围 0-255,人的寿命不会超过 255 岁);海龟就必须是 smallint,但如果是太阳的年龄,就必须是 int;如果是所有恒星的年龄都加起来,那么就必须使用 bigint。

索引

  1. SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts最好。

    说明:

    1)consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。

    2)ref 指的是使用普通的索引。(normal index)

    3)range 对索引进范围检索。

  2. 页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。

    索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。

  3. 建组合索引的时候,区分度最高的在最左边。

    正例:如果 where a=? and b=? ,a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。

-------------本文结束感谢您的阅读-------------