• 代码规范整理


    项目组代码规范

    更新说明

    版本号更新说明更新人更新时间
    1.0建立文档Reed2022-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(); 
        } 
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、
      获取数据库连接,进行不必要的 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);
      
      • 1
      • 2
      • 3
    • 对 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);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 避免重复打印日志,浪费磁盘空间,务必在 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两数进行计算,类型不对可能导致结果错误。浮点数的精度丢失问题。

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



  • 相关阅读:
    雷电4模拟器安装xposed框架(2022年)
    vue.js毕业设计,基于vue.js前后端分离图书购物商城系统设计与实现(H5移动项目)
    Java对象深拷贝 终极方案 deep clone an object
    C#服务器端代码
    交叉导轨在自动化设备中的作用
    ARM mkv210_image.c 文件详解
    2022最新上传ipa到appstore的步骤说明
    总结的HTTP比较详细的知识
    Python之numpy数组篇(上)
    【Highway-env】IntersectionEnv代码阅读
  • 原文地址:https://blog.csdn.net/qq_40184195/article/details/127433303