开发规范 2021版本

Posted on 2023-03-20 13:50  林安静  阅读(28)  评论(0编辑  收藏  举报

个人感觉就是参考的《阿里编程规范手册》...

一、命名

  1. JDK统一使用JDK8,因需要支持WAS,编译输出级别需为1.6(代码风格请使用JDK6风格,JDK7以上风格会导致编译不通过)。编译统一使用maven,maven中会有编译需要的一切配置,禁止使用本地配置编译代码。

  2. 禁止对jdk自带的sun包的调用

  3. 提交前要进行比较,确认修改,不允许不比较就提交代码。

  4. 不允许对全代码进行Format,以免无法对比。

  5. 禁止使用TortoiseSVN(俗称小乌龟)提交代码。

  6. 强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。

  7. 强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。

  8. 强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。

  9. 强制】类型与中括号紧挨相连来表示数组。

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

  11. 强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使 用单数形式,但是类名如果有复数含义,类名可以使用复数形式

  12. 强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。

  13. 强制】项目包结构约束,禁止直接在 com.seeyon.下进来代码编写

  14. 18.【参考】各层命名规约:

    A) Service/DAO 层方法命名规约 :

    1) 获取单个对象的方法用 get / select 做前缀, 如:get / getOne / selectOne。

    2) 获取多个对象的方法用 list/select 做前缀,复数形式结尾,如:listObjects / selectList。

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

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

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

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

    7) 检查带参查询, 如:getBeanByName / getNameById

    B) 领域模型命名规约 :

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

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

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

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


二、常量定义

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

  2. 推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、 包内共享常量、类内共享常量。

    1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。

    2) 应用内共享常量:放置在一方库中,通常是子模块中的 constant 目录下。

    正确示范

    错误示范:易懂变量也要统一定义成应用内共享常量,两位工程师在两个类中分别定义了“YES”的变量:

    类 A 中:

      public static final String YES = "yes"; 
    

    类 B 中:

      public static final String YES = "y"; 
    
      // 预期是 true,但实际返回为 false,导致线上问题。
      A.YES.equals(B.YES), 
    

    3) 子工程内部共享常量:即在当前子工程的 constant 目录下。

    4) 包内共享常量:即在当前包下单独的 constant 目录下。

    5) 类内共享常量:直接在类内部 private static final 定义。

  3. 推荐】如果变量值仅在一个固定范围内变化用 enum 类型来定义。

    说明:如果存在名称之外的延伸属性应使用 enum 类型,下面正例中的数字就是延伸信息,表示一年中的第几个季节。 并且枚举中仅提供get方法,不提供set方法

    正确示范

    public enum SeasonEnum { 
       SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4); 
       private int seq; 
       SeasonEnum(int seq) { 
          this.seq = seq; 
       } 
      public int getSeq() { 
         return seq;
      } 
    }
    

三、代码格式

  1. 强制】if/for/while/switch/do 等保留字与括号之间都必须加空格。

  2. 强制】任何二目、三目运算符的左右两边都需要加一个空格。

  3. 强制】采用 4 个空格缩进,禁止使用 tab 字符

  4. 强制注释的双斜线与注释内容之间有且仅有一个空格

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

    1)第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。

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

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

    4)方法调用中的多个参数需要换行时,在逗号后进行。

    5)在括号前不要换行,见反例。

         StringBuilder sb = new StringBuilder(); 
         // 超过 120 个字符的情况下,换行缩进 4 个空格,点号和方法名称一起换行
         sb.append("Jack").append("Ma")... .
                .append("seeyon")... .
                .append("seeyon"")... .
                .append("seeyon"");
    

四、OOP规约

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

    正确示范

             public class SeeyonStatic {
    
             public static final String TEST_STRING = "test_string";
    
             }
    
             SeeyonStatic.TEST_STRING
    
  2. 强制】为了防止精度损失,禁止使用构造方法 BigDecimal(double)的方式把 double 值转 化为 BigDecimal 对象。

    说明:BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。

    如:BigDecimal g = new BigDecimal(0.1f); 实际的存储值为:0.10000000149

    正确示范:优先推荐入参为 String 的构造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其实执行了 Double 的 toString,而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。

          BigDecimal recommend1 = new BigDecimal("0.1"); 
    
          BigDecimal recommend2 = BigDecimal.valueOf(0.1); 
    
  3. 强制】序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如果 完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。 说明:注意 serialVersionUID 不一致会抛出序列化运行时异常。

  4. 强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。

  5. 强制】POJO 类必须写 toString 方法。使用 IDE 中的工具:source> generate toString 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。

    说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。

  6. 推荐】使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内 容的检查,否则会有抛 IndexOutOfBoundsException 的风险。

    说明

       String str = "a,b,c,,"; 
       String[] ary = str.split(","); 
       // 预期大于 3,结果是 3 
       System.out.println(ary.length); 
    
  7. 【推荐】 类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter 方法。

  8. 推荐】final 可以声明类、成员变量、方法、以及本地变量,下列情况使用 final 关键字:

    1) 不允许被继承的类,如:String 类。

    2) 不允许修改引用的域对象。

    3) 不允许被覆写的方法,如:POJO 类的 setter 方法。

    4) 不允许运行过程中重新赋值的局部变量。

    5) 避免上下文重复使用一个变量,使用 final 可以强制重新定义一个变量,方便更好地进行重构。

  9. 推荐】类成员与方法访问控制从严:

    1) 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。

    2) 工具类不允许有 public 或 default 构造方法。

    3) 类非 static 成员变量并且与子类共享,必须是 protected。

    4) 类非 static 成员变量并且仅在本类使用,必须是 private。

    5) 类 static 成员变量如果仅在本类使用,必须是 private。

    6) 若是 static 成员变量,考虑是否为 final。

    7) 类成员方法只供类内部调用,必须是 private。

    8) 类成员方法只对继承类公开,那么限制为 protected。

    说明:任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦。思考:是一个private 的方法,想删除就删除,可是一个 public 的 service 成员方法或成员变量,删除一下,不 得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,无限制的到处跑,那么你会担心的。

  10. 1

  11. 1

五、集合处理

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

  2. 强制】泛型通配符<?extendsT>来接收返回的数据,此写法的泛型集合不能使用add方 法,而<? super T>不能使用 get 方法,作为接口调用赋值时易出错。

    说明:扩展说一下 PECS(Producer Extends Consumer Super)原则:

    第一、频繁往外读取内容的,适合 用<? extends T>。

    第二、经常往里插入的,适合用<? super T>

六、并发处理

  1. 特殊说明:并发相关问题,研发人员都需要和直线经理沟通,直线经理不能解决可以和业务线的架构师沟通解决。并发相关的重要原则:能不使用锁解决就不使用锁,能使用乐观锁就不使用悲观锁,用锁就需要业务线的架构review代码并提供对锁使用的评估和锁使用的方案

  2. 强制】SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。

    说明:如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。

    正确示范:注意线程安全,使用 DateUtils。亦推荐如下处理:

               private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { 
                      @Override
                      protected DateFormat initialValue() {
                          return new SimpleDateFormat("yyyy-MM-dd");
                      } 
               };
    

七、控制语句

  1. 强制】表达异常的分支时,少用if-else方式,这种方式可以改写成:

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

    说明:如果非使用 if()…else if()…else…方式表达逻辑,避免后续代码维护困难,【强制】请勿超过 3 层。

    正确示范:超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句 即代码逻辑先考虑失败、异常、中断、退出等直接返回的情况,以方法多个出口的方式,解决代码中判断 分支嵌套的问题,这是逆向思维的体现。

    示例如下:

        public void findBoyfriend(Man man) {
          if (man.isUgly()) { 
              System.out.println("本姑娘是外貌协会的资深会员");
              return; 
          }
    
          if (man.isPoor()) { 
             System.out.println("贫贱夫妻百事哀");
              return; 
          }
    
         if (man.isBadTemper()) { 
             System.out.println("银河有多远,你就给我滚多远");
             return; 
         }
    
         System.out.println("可以先交往一段时间看看"); 
      }
    

八、注释规约

  1. 【参考】谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。

  2. 【参考】对于注释的要求:第一、能够准确反映设计思想和代码逻辑;第二、能够描述业务 含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者 形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继 任者看的,使其能够快速接替自己的工作。

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

    \1) 待办事宜(TODO):(标记人,标记时间,[预计处理时间]) 表示需要实现,但目前还未实现的功能。这实际上是一个Javadoc 的标签,目前的 Javadoc 还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 Javadoc标签)。

    \2) 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。

九、异常

  1. 强制】Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通 过 catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException 等 等。

    说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,可能存在数字格式错误,不得不 通过 catch NumberFormatException 来实现。

    正确示范:if (obj != null) {...}

    错误示范:try { obj.method(); } catch (NullPointerException e)

  2. 强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它, 请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理 解的内容。

  3. 强制】finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。

    说明:如果 JDK7 及以上,可以使用 try-with-resources 方式。

  4. 强制】不要在 finally 块中使用 return。

    说明:try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存 在 return 语句,则在此直接返回,无情丢弃掉 try 块中的返回点。

    正确示范

    错误示范

        private int x = 0; 
        public int checkReturn() { 
           try { 
              // x 等于 1,此处不返回 
              return ++x; 
           } finally {
             // 返回的结果是 2 
             return ++x; 
           } 
        }
    
  5. 强制】在调用 RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable 类来进行拦截。

  6. 推荐】定义时区分 unchecked / checked 异常,避免直接抛出 new RuntimeException(), 更不允许抛出 Exception 或者 Throwable,应使用有业务含义的自定义异常。推荐业界已定 义过的自定义异常,如:DAOException / ServiceException 等。

  7. 1

  8. 1

  9. 1

十、其它

  1. 强制】后台输送给页面的变量必须加$!{var}——中间的感叹号。

  2. 强制】注意 Math.random() 这个方法返回是 double 类型,注意取值的范围 0≤x<1(能够 取到零值,注意除零异常),如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后 取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。

  3. 强制】获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime();

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

  4. 表示日期和时间的格式如下所示: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");