代码规范整理

项目组代码规范

更新说明

版本号 更新说明 更新人 更新时间
1.0 建立文档 Reed 2022-05-19

命名

  • 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从
    驼峰形式。杜绝完全不规范的缩写,避免望文不知义。

    反例:AbstractClass“缩写”命名成 AbsClass;condition“缩写”命名成 condi,此类随
    意缩写严重降低了代码的可阅读性。

  • 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
    正例:MAX_STOCK_COUNT
    反例:MAX_COUNT

  • 全局变量尽量做到见名知意,并且必须增加注释说明;局部变量看情况而定

  • 前后端代码目录结构是否保持对称?

    命名规则是否与公司的规范保持一致?

    文件名是否遵循项目组约定?

    是否存在重名问题?

    前端的变量、方法,使用以交易名称的组合,例如:modify_xxx;尽量避免方法名带数字,例如: xxx1

  • 类名是否存在重名问题?(自己实现的类尽量不要和别人的类重名,尽管不在同一个包下。特别注意子类和父类重名的情况。)

  • 包名统一使用小写

  • POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。
    反例:定义为基本数据类型 Boolean isDeleted;的属性,它的方法也是 isDeleted(),部分
    框架在反向解析的时候,“以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异
    常。

  • 项目分层

    Action:接收前端请求、处理抛出异常、参数校验
    Service:逻辑处理
    Dao:和数据库交互

    Adapter:和外部接口交互

    • Service/DAO 层方法命名规约
      1. 获取单个对象的方法用 query 做前缀。
      2. 获取多个对象的方法用 list 做前缀。
      3. 获取统计值的方法用 count 做前缀。
      4. 插入的方法用 save/insert 做前缀。
      5. 删除的方法用 remove/delete 做前缀。
      6. 修改的方法用 update 做前缀。

注释

  • 类注释是否完整及正确?(用途、作者、版本、时间等信息)

  • 注释是否较清晰且必要?

    对于注释的要求:

    1. 能够准确反应设计思想和代码逻辑;
    2. 能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。
    3. 好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的
      一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
  • 复杂的分支流程是否已经被注释?复杂的逻辑代码是否有比较详细的注释说明。

  • 函数是否已经有注释?文件,类(含接口,枚举等),成员变量,方法前需要有JAVADOC的注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。

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

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

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

    说明:代码被注释掉有两种可能性:

    1. 后续会恢复此段代码逻辑。

    2. 永久不用。

      前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。

  • 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑
    等的修改。修改时需要标注修改人、修改时间、修改内容

    正例:// 修改内容 modify by Reed 20220519

格式化

  • 所有的代码必须进行格式化,以保证样式整齐,便于查看

  • 左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格

    正例:if (a == b)

    反例:if (空格 a == b 空格)

  • 任何二目、三目运算符的左右两边都需要加一个空格。

    正例:a ? b : c;

  • 运算符的左右必须有一个空格 (说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等)
    int flag = 0;

  • 注释的双斜线与注释内容之间有且仅有一个空格。
    正例:// 注释内容,注意在//和注释内容之间有一个空格。

  • 方法参数在定义和传入时,多个参数逗号后边必须加空格。
    正例:method(“a”, “b”, “c”);

  • 方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义
    之间插入一个空行。

  • 在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码,避免采用
    单行的编码方式:if (condition) statements;

    正例:

    if(){

    } else if(){

    }

控制语句

  • 单行是否只有单个功能?
  • 单个函数是否执行了单个功能并与其命名相符?
  • 在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程
    序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且
    放在最后,即使它什么代码也没有。

可靠性(语句/变量)

  • 常数变量是否声明为final?

  • 成员变量、局部变量是否在使用前被赋值? 对象初始化为null的对象被调用前必须被重新赋值,如果赋值语句在try块中,调用操作必须在try块中。

  • 对数组的访问是否是安全的?(合法的index取值为[0, MAX_SIZE-1])。

  • 所有判断是否都使用了(常量==变量 或者 常量.equals(变量))的形式?常量放在比较符前可以有效降低比较符写成赋值语句 ,减少空指针异常。

    正例:“test”.equals(object);
    反例:object.equals(“test”);

  • 对于流操作代码的异常捕获是否有finally操作以关闭流对象?

  • 对浮点数值的相等判断是否是恰当的?严禁使用==直接判断浮点数值 。

  • 是否对象比较都使用了equals?而不是使用==或!=操作。

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

  • 包装类做简单预算前是否保证非空?建议进行非空(null != xx)判断,防止发生空指针异常。

  • 对参数的非空判断必须出现在方法调用之前,否则说明前面可能导致空指针或者后者判断是没有必要的,非空判断,默认由调用者提供。

  • 非线程安全的对象是否被正确保证线程安全?

  • 禁止使用class控制页面元素选择,必须要使用id进行唯一性选择

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

    // 正例:
    Iterator iterator = list.iterator(); 
    while (iterator.hasNext()) { 
      String item = iterator.next(); 
      if (删除元素的条件) { 
      	iterator.remove(); 
      } 
    }
    
  • 循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、
    获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)。

  • 死循环控制,要有个阀值

  • if条件判断是否都采用”==”号,是否有写成赋值”=”号。

  • if条件判断表达式,判断值放在““”==”前面,如:if(“N03”==count)。

可靠性(方法)

  • 入口对象是否都被进行了判断不为空?

  • 入口数据的合法范围是否都被进行了判断?

    接口入参保护,这种场景常见的是用于做批量操作的接口。

    • 下列情形,需要进行参数校验:
      1. 调用频次低的方法。
      2. 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参
        数错误导致中间执行回退,或者错误,那得不偿失。
      3. 需要极高稳定性和可用性的方法。
      4. 对外提供的开放接口,不管是 RPC/API/HTTP 接口。
      5. 敏感权限入口。
    • 下列情形,不需要进行参数校验:
      1. 极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查要求。
      2. 底层调用频度比较高的方法。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底
        层才会暴露问题。一般 DAO 层与 Service 层都在同一个应用中,部署在同一台服务器中,所
        以 DAO 的参数校验,可以省略。
      3. 被声明成 private 只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参
        数已经做过检查或者肯定不会有问题,此时可以不校验参数。

    说明:忽略参数校验可能导致:

    • page size 过大导致内存溢出
    • 恶意 order by 导致数据库慢查询
    • 任意重定向
    • SQL 注入
    • 反序列化注入
    • 正则输入源串拒绝服务 ReDoS(Java 代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。)
  • 是否对有异常抛出的方法都执行了try…catch保护?

  • 是否函数的所有分支都有返回值?

  • int的返回值是否符合项目组约定?如成功为0000,其他均为失败。

  • 是否对方法返回值对象做了null检查,该返回值定义时是否被初始化?

  • 线程处理函数循环内部是否有异常捕获处理,防止线程抛出异常而退出?

  • 方法对错误的处理是否是恰当的?

  • 异常捕获后是否进行了日志记录或异常继续抛出?

  • 导致结构模糊的连续赋值是否已经修改?如:(a= (b=d+c ))

  • 所有的覆写方法,必须加@Override 注解。
    说明:getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加@Override
    可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编
    译报错

代码警告

  • 是否清除了只定义未使用的局部变量?
  • 是否已经消除了所有警告?

日志

  • 打印信息是否都用日志管理?不要使用System.out.println打印信息。

  • 是否正确使用了日志记录?关键代码日志打印级别正确及不缺失。

  • 在输出对象类型时,必须写 toString 方法。使用 IDE 的中工具:source> generate toString
    时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。
    说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排
    查问题

  • 日志记录的Log,Logger对象是否定义为常量?用于记录日志的Log,Logger对象在类中定义必须是static final的,建议定义为private的,因为这类对象初始化比较耗时,不利系统运行。

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

    import org.slf4j.Logger; 
    import org.slf4j.LoggerFactory;
    private static final Logger logger = LoggerFactory.getLogger(Abc.class);
    
  • 对 trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式。
    说明:logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
    如果日志级别是 warn,上述日志不会打印,但是会执行字符串拼接操作,如果 symbol 是对象,会执行 toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。

    // 正例:(条件)
    if (logger.isDebugEnabled()) { 
    	logger.debug("Processing trade with id: " + id + " and symbol: " + symbol); 
    } 
    
    // 正例:(占位符)
    logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
    
  • 避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false。
    正例:

  • 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过
    关键字 throws 往上抛出。
    正例:logger.error(各类参数或者对象 toString + “_” + e.getMessage(), e);

  • 谨慎地记录日志。

    生产环境禁止输出 debug 日志;有选择地输出 info 日志;

    如果使用 warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘

    撑爆,并记得及时删除这些观察日志。

    说明:大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。记录日志时请

    思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?

    使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。注意日志输出的级别,error 级别只记录系统逻辑出错、异常等重要的错误信息。如非必要,请不要在此场景打出 error 级别。

复杂度

  • 单个类行数是否不大于500行?单个类建议行数小于500行,最多不超过1000行。
  • 方法参数个数是否在7个以内?方法参数个数建议不大于5个,最多不超过7个。
  • 单个方法函数是否不大于30行?单个方法建议函数不大于30行,最多不超过60行。
  • 单方法中try/for/while/switch/if最深深度是否不大于5?单方法中try/for/while/switch/if最深深度不允许大于5。

异常

  • 是否对有可能抛出异常的方法都执行了try…catch。
  • 有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。
  • finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
    说明:如果 JDK7 及以上,可以使用 try-with-resources 方式。
  • 不能在 finally 块中使用 return,finally 块中的 return 返回后方法结束执行,不
    会再执行 try 块中的 return 语句。

可维护性

  • 不允许任何魔法值(即未经定义的常量)直接出现在代码中。
    反例:

    String key = “Id#taobao_” + tradeId;
    cache.put(key, value);

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

    淆,造成误解。
    说明:Long a = 2l; 写的是数字的 21,还是 Long 型的 2?

  • java数组定义如下:String[] args

  • 避免出现重复的代码(Don’t Repeat Yourself),即 DRY 原则。
    说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副
    本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是组件化。
    正例:一个类中有多个 public 方法,都需要进行数行相同的参数校验操作,这个时候请抽取:
    private boolean checkParam(DTO dto) {…}

  • 待办事宜(TODO):( 标记人,标记时间,[预计处理时间])
    表示需要实现,但目前还未实现的功能。

  • 类成员与方法访问控制,如果仅在本类使用,必须是 private

  • UDE部门提供的样式文件不允许进行修改。若需要修改,项目组约定新建文件或者与公司的美工进行同步。

其他

  • IDE 的 text file encoding 设置为 UTF-8,防止编码格式不一致导致的中文乱码。

  • 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放限制,
    如数量限制、疲劳度控制、验证码校验,避免被滥刷、资损。
    说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其
    它用户,并造成短信平台资源浪费

  • 用户请求传入的任何参数必须做有效性验证。

  • 界面显示的敏感信息是否已经按要求进行脱敏。包括卡号、账号、证件号、金额、姓名等。

    说明:查看个人手机号码会显示成:1589119,隐藏中间 4 位,防止隐私泄露。

  • 金额显示是否进行格式化。金额格式化无错误。

  • ajax请求全部采用异步方法,防止页面卡死。

  • 代码中是否使用Debug参数控制调试代码,在正常逻辑代码中是不存在调试代码?

  • 客户输入的金额有校验机制。

  • 上送交易的金额,没有经过中间处理,提交记账金额与客户输入金额一致

  • 界面只要涉及提交数据到后台的按钮,有防重复提交机制且有效。如页面控制,重复流水号检查机制等。

  • 涉及数据计算的代码确定计算结果无误?Js两数进行计算,类型不对可能导致结果错误。浮点数的精度丢失问题。

  • 页面跳转或与后端请求按钮触发后是否显示遮罩,显示等待提示。



你可能感兴趣的:(Java,代码规范,数据库,java)