集团开发规约学习总结

开发规约总结

1.POJO类中的任何布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列列化错误。

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

3.枚举类名带上Enum后缀,枚举成员名称需要全大写,单词间⽤下划线隔开。

4.各层命名规约:
 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。

5.不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。
反例:String key = "Id#taobao_" + tradeId;
   cache.put(key, value);
本例中同学A定义了缓存的key,然后缓存提取的同学B使⽤了Id#taobao来提取,少了下划线,导致故障。

6.要使⽤一个常量类维护所有常量,要按常量功能进行归类,分开维护。
正例:缓存相关的常量放在类CacheConsts下;系统配置相关的常量放在类ConfigConsts下。

7.不同逻辑、不同语义、不同业务的代码之间插⼊一个空⾏分隔开来以提升可读性。

8.所有整型包装类对象之间值的比较,全部使用equals⽅法比较。
说明:对于Integer var=?在-128⾄127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使⽤==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是⼀个⼤坑,推荐使用equals⽅法进⾏判断。

9.浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用equals来判断。
因为浮点数采⽤用“尾数+阶码”的编码⽅方式,二进制⽆法精确表示大部分的十进制小数
float精度计算原理:链接
正例:
(1) 指定⼀一个误差范围,两个浮点数的差值在此范围之内,则认为是相等的

          float a = 1.0f - 0.9f;
          float b = 0.9f - 0.8f;
          float diff = 1e-6f;
          if (Math.abs(a - b) < diff) {
              System.out.println("true");
          }

(2) 使⽤用BigDecimal来定义值,再进⾏行行浮点数的运算操作

          BigDecimal a = new BigDecimal("1.0");
          BigDecimal b = new BigDecimal("0.9");
          BigDecimal c = new BigDecimal("0.8");
          BigDecimal x = a.subtract(b);
          BigDecimal y = b.subtract(c);
          if (x.equals(y)) {
              System.out.println("true");
          }

10.禁⽌使用构造方法 BigDecimal(double) 的方式把 double值 转化为 BigDecimal对象
说明:BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
如:BigDecimal g = new BigDecimal(0.1f); 实际的存储值 为:0.100000001490116119384765625
优先推荐入参为String的构造方法,或使用BigDecimal的valueOf⽅方法,此方法内部其实执行了Double的 toString,而Double的toString按double的实际能表达的精度对尾数进行了截断。

          BigDecimal good1 = new BigDecimal("0.1");
          BigDecimal good2 = BigDecimal.valueOf(0.1);

11.使用索引访问用String的split⽅法得到的数组时,需做最后一个分隔符后有⽆内容的检查,否则会有抛IndexOutOfBoundsException的风险。
说明:

      String str = "a,b,c,,";
      String[] ary = str.split(",");
      // 预期⼤大于3,结果是3
      System.out.println(ary.length);

12.循环体内,字符串的联接⽅式,使用StringBuilder的append方法进⾏行扩展。
反例:

      String str = "start";
      for (int i = 0; i < 100; i++) {
           str = str + "hello";
      }

说明:反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作, 最后通过toString方法返回String对象,造成内存资源浪费。
String和StringBuilder对比原理:链接

13.慎⽤Object的clone⽅法来拷贝对象。
说明:对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现域对象的深度遍历式拷⻉。
clone拷贝:链接

14.Collections类返回的对象,如:emptyList()/singletonList()等都是immutable list,不可对其进行添加或者 删除元素的操作。
反例:某⼆⽅库的⽅法中,如果查询无结果,返回Collections.emptyList()空集合对象,调⽤方⼀旦进行了添加 元素的操作,就会触发UnsupportedOperationException异常。

15.ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ;
说明: subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,⽽是 ArrayList 的一个视图,对于 SubList子列表的所有操作最终会反映到原列表上。

16.在subList场景中,⾼度注意对父集合元素的增加或删除,均会导致子列表的遍历、增加、删除产⽣ConcurrentModification Exception 异常。

List list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
List ll = list.subList(0, 3);
list.add(5);
//会抛出ConcurrentModificationException异常
System.out.println(ll.size());

17.使⽤集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大⼩就是 list.size()。
反例:
直接使⽤用toArray⽆无参⽅方法存在问题,此⽅方法返回值只能是Object[]类,若强转其它类型数组将出现ClassCastException错误。
正例:

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

18.在使用Collection接口任何实现类的addAll()方法时,都要对输入的集合参数进行NPE判断。
说明:在ArrayList.addAll⽅法的第⼀⾏代码即 Object[] a = c.toArray();
其中c为输⼊集合参数,如果为null,则直接抛出异常。

19.使⽤⼯具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear
方法会抛出UnsupportedOperationException异常。
说明:asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器式, 只是转换接口,后台的数据仍是数组。
String[] str = new String[] { "a", "b" };
List list = Arrays.asList(str);
第一种情况:list.add("c"); 运行时异常。
第二种情况:str[0]= "changed"; 那么list.get(0)也会随之修改,反之亦然。

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

 List list = new ArrayList<>();
      list.add("1");
      list.add("2");
      for (String item : list) {
          if ("1".equals(item)) {
              list.remove(item);
          }
}

说明:把上面代码中的"1".equals(item)换成"2"会抛出ConcurrentModificationException异常

抛出异常原因和foreach原理:链接
正例:

      Iterator iterator = list.iterator();
      while (iterator.hasNext()) {
            String item = iterator.next(); 
            if (删除元素的条件) {
              iterator.remove();
            }
      }

21.在JDK7版本以上,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⽐比结果相同。
JDK7/JDK8的Collections.Sort,Arrays.sort方法实现中,如果两个值是相等的,那么compare方法需要返回0,否则可能会在排序时抛错,而JDK6是没有这个限制的。
反例:下例例中没有处理理相等的情况,实际使⽤用中可能会出现异常:

new Comparator() {
          @Override
          public int compare(Student o1, Student o2) {
              return o1.getId() > o2.getId() ? 1 : -1;
} }

22.集合初始化时,指定集合初始值⼤小。
说明:如果暂时⽆无法确定集合⼤大⼩小,那么指定默认值(16)即可:

反例:HashMap需要放置1024个元素,由于没有设置容量初始⼤小,随着元素不断增加,容量7次被迫扩⼤,
需要重建hash表,严重影响性能。

23.表达异常分支时,少⽤if-else方式,这种⽅式可以改写成:

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

说明:如果非得使用if()...else if()...else...⽅式表达逻辑,
【强制】请勿超过3层,超过请使⽤用状态设计模式。
正例:超过3层的 if-else 的逻辑判断代码可以使⽤卫语句、策略模式、状态模式等来实现,其中卫语句 示例如下:

  public void today() {
        if (isBusy()) {
            System.out.println("change time.");
            return; 
         }

         if (isFree()) {
             System.out.println("go to travel.");
             return;
         }
       System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");
       return;
    }

24.避免⽤ApacheBeanutils进行属性的copy。
说明:Apache BeanUtils性能差,可以使⽤其他方案⽐如Spring BeanUtils, Cglib BeanCopier, 注意均是浅拷贝。

25.获取当前毫秒数:System.currentTimeMillis();
⽽不是new Date().getTime();
说明:如果想获取更更加精确的纳秒级时间值,使⽤用System.nanoTime的⽅方式。
在JDK8中,针对统计时间等场景,推荐使用Instant类。

26.防⽌NPE,是程序员的基本修养,注意NPE产⽣生的场景:
 1) 返回类型为基本数据类型,return包装数据类型的对象时,⾃自动拆箱有可能产⽣生NPE。
反例:public int f(){ return Integer对象},如果为null,⾃自动解箱抛NPE。

 2) 数据库的查询结果可能为null。
 3) 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
 4) 远程调⽤返回对象时,⼀律要求空指针判断,防止NPE。
 5) 对于session中获取的数据,建议进行NPE检查,避免空指针。
 6) 级联调⽤obj.getA().getB().getC();易产⽣生NPE。

27.应⽤中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架(SLF4J、JCL--Jakarta Commons Logging)中的API。什么是日志框架和日志系统,请参考webx作者宝宝的⽂文章
(链接),文章里 也详细说明了为什么不能直接依赖使用⽇志系统⽽是⽇志框架,以及应用的pom中如何做 dependencyManagement。

说明:⽇日志框架(SLF4J、JCL--Jakarta Commons Logging)的使⽤用⽅方式(推荐使⽤用SLF4J)

28.日志⽂件推荐⾄少保存15天,因为有些异常具备以“周”为频次发生的特点。对于当天日志,以“应⽤用 名.log”来保存,保存在/home/admin/应⽤用名/logs/⽬目录下,过往⽇日志格式为: {logname}.log.{保存 ⽇日期},⽇日期格式:yyyy-MM-dd
说明:以mppserver应⽤用为例例,日志保存在/home/admin/mppserver/logs/mppserver.log,历史⽇日志 名称为
mppserver.log.2016-08-01

29.在日志输出时,字符串变量之间的拼接使用占位符的⽅式。
说明:因为String字符串的拼接会使用StringBuilder的append()方式,有一定的性能损耗。使用占位符仅是替换 动作,可以有效提升性能。
正例:

  logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);

30.表达是与否概念的字段,必须使用is_xxx的⽅式命名,数据类型是unsigned tinyint(1表示是,0表示否),
此规则同样适⽤用于odps建表。
说明:任何字段如果为⾮负数,必须是unsigned。
注意:POJO类中的任何布尔类型的变量量,都不不要加is前缀,所以,需要在设置从is_xxx到 Xxx的映射关系。数据库表示是与否的值,使⽤用tinyint类型,坚持is_xxx的命名⽅方式是为了了明确其取值含义 与取值范围。
正例:表达逻辑删除的字段名 is_deleted ,1表示删除,0表示未删除。

31.表名、字段名必须使用⼩写字母或数字,字段命名可参考附2;禁止出现数字开头,禁⽌两个下划线中间只
出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
说明:MySQL在Windows下不区分大小写,但在Linux下默认是区分⼤小写。因此,数据库名、表名、字段名, 都不允许出现任何⼤写字母,避免节外⽣枝。
正例:getter_admin,task_config,level3_name

32.禁⽤保留字,如desc、range、match、delayed等,参考官方保留字 链接

33.唯一索引名为uk_字段名;普通索引名则为idx_字段名。
说明:uk_ 即 unique key;idx_ 即index的简称。
索引知识点:链接
explain使用字段说明:链接
加深最左原则理解:链接
不能用到索引情况:链接

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

35.varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类 型为TEXT,独⽴出来一张表,用主键来对应,避免影响其它字段索引效率。

36.业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。
说明:不要以为唯一索引影响了insert速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应 ⽤层做了非常完善的校验和控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。

37.超过三个表禁止join。需要join的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要 有索引。

38.在varchar字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索 引⻓度。
说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为20的索引,区分度会高达90%以 上,可以使用count(distinct left(列列名, 索引⻓长度))/count(*)的区分度来确定。0.31为黄金值

39.如果有order by的场景,请注意利⽤索引的有序性。order by最后的字段是组合索引的一部分,并且放在索 引组合顺序的最后,避免出现file_sort的情况,影响查询性能。
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引中有范围查找,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引a_b⽆法排序。

40.利用覆盖索引来进行查询操作,来避免回表操作。
说明:如果一本书需要知道第11章是什么标题,会翻开第11章对应的那⼀⻚吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。如果索引包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index),也就是 平时所说的不需要回表操作
正例:IDB能够建⽴索引的种类分为【主键索引、唯一索引、普通索引】,而覆盖索引是一种查询的一种效果, ⽤ explain的结果,extra列列会出现:using index.

41.利用延迟关联或者子查询优化超多分页场景。
说明:MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别 大的时候,效率就⾮常的低下,要么控制返回的总⻚数,要么对超过特定阈值的页数进行SQL改写。
正例:先快速定位需要获取的id段,然后再关联:
SELECT a.* FROM 表1 a, (select id from 表1 where 条件 LIMIT 100000,20 ) b where a.id=b.id

42.建组合索引的时候,区分度最高的在最左边。
正例:如果where a=? and b=? ,a列的几乎接近于唯一值,那么只需要单建idx_a索引即可。
说明:存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。
如:where c>? and d=? 那么即使c的区分度更高,也必须把d放在索引的最前列,即建立组合索引idx_d_c。

43.不要使用count(列名)或count(常量)来替代count(),count()就是SQL92定义的标准统计行数的语法,跟 数据库⽆关,跟NULL和⾮NULL⽆关。
说明:count(*)会统计值为NULL的行,⽽count(列名)不会统计此列为NULL值的⾏。

44.count(distinct col) 计算该列除NULL之外的不重复数量。
注意:count(distinct col1, col2) 如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。

45.当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,因此使用sum() 时需注意NPE问题。
正例:可以使用如下⽅式来避免sum的NPE问题:SELECT IFNULL(SUM(column), 0) FROM table;

46.使用ISNULL()来判断是否为NULL值。
 说明: NULL 与任何值的直接比较都为 NULL 。
  1) NULL<>NULL 的返回结果是 NULL ,⽽不是 false 。
  2) NULL=NULL 的返回结果是 NULL ,⽽不是 true 。
   3) NULL<>1 的返回结果是 NULL ,⽽不是 true 。

47.在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分⻚语句。

48.不要用resultClass当返回参数,即使所有类属性名与数据库字段⼀一对应,也需要定义;反过来,每一个表 也必然有一个与之对应。
说明:配置映射关系,使字段与DO类解耦,⽅方便便维护。

49.工具类二⽅库已经提供的,不要在本应⽤中编程实现。
json操作: fastjson md5操作:commons-codec
⼯具集合:Guava包
数组操作:ArrayUtils(org.apache.commons.lang3.ArrayUtils)
集合操作:CollectionUtils(org.apache.commons.collections4.CollectionUtils)
除上面以外还有NumberUtils、DateFormatUtils、DateUtils等优先使⽤用org.apache.commons.lang3 这个包下的,不要使用org.apache.commons.lang包下⾯的。
原因是commons.lang这个包是从JDK1.2 开始支持的所以很多1.5/1.6的特性是不⽀持的,例如:泛型。

你可能感兴趣的:(集团开发规约学习总结)