编码规范
本文最后更新于 1436 天前,其中的信息可能已经有所发展或是发生改变。

为什么要有编码规范

编码规范对于程序员而言尤为重要,有以下几个原因:

  • 一个软件的生命周期中,80%的花费在于维护

  • 几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护

  • 编码规范可以改善软件的可读性,可以让程序员尽快而彻底地理解新的代码

  • 如果你将源码作为产品发布,就需要确任它是否被很好的打包并且清晰无误,一如你已构建的其它任何产品

为了执行规范,每个软件开发人员必须一致遵守编码规范

遵循阿里的的编码规范(P3C)

什么是代码复查(Code Review)?

  • 代码复查又叫“代码审查”,其基本思想是在开发人员编写完自己的代码后,由其他人进行复查,检查代码中存在的问题。

  • p代码复查的一个基本理论是,当越早发现代码存在的缺陷,解决缺陷的代价就越低

代码复查往往分成以下一个方面进行审查:

  • 代码风格

    • 代码格式
  • 重大缺陷
    • SQL注入问题
    • 性能问题
  • 设计逻辑与思路的审查
    • 代码逻辑控制是否有问题
    • 代码是否与最初的设计存在出入

代码复查常见问题

1.代码格式

  • 未安装静态代码检查工具
  • 代码的缩进仍然使用TAB键,而不是空格
  • 未使用快捷键“Ctrl+Shift+F”格式化

2.代码注释

  • 存在大段代码没有注释的情况,最好有简单的注释说明一下。
  • 程序的重要分支没有注释,分支的注释有利于理解业务逻辑。
  • 代码注释与实现不一致。
  • 代码注释多余或者没有实际意义。
  • 常量注释最好采用多行注释的风格而非单行注释。
    • 说明:这样做的原因是,在IDE中,外部调用常量的地方可以看到注释

3.命名规范

  • 常量定义问题,很多数字(魔法数字)、字符应该定义为常量,并指定有意义的名称,便于理解和维护

4.代码结构

  • 程序没有分层的概念
  • 每层的职责要分明
  • 类方法的参数列表过长,应该抽象为一个参数类
  • 存在多层if嵌套和多层while嵌套的情况

  • 无效的引用,代码中存在很多无效的包、类引用,应该去掉没用到的引用,或者使用快捷键CTRL+SHIFT+O重新组织一下导入

  • 无效代码

    • 存在空的方法或者没用到的变量,建议删除。

    • 一些业务处理类的方法参数中有pageNo相关的参数,但是实际上该函数并没有进行分页处理。

    • 复查时,经常看到大段的代码被注释掉,建议删除。 咱们有svn,如果想找到,可以查看svn历史版本。

第一章 正确性检查

1. 数据类型处理

  • 定义了一个Integer类型的a,在对a进行switch(a)等操作的时候,没有对a进行判空,导致空指针异常

  • double数字处理时,直接进行加减、比较、零值判断等,可能存在精度问题。正确的判断方式应是约等于零,这个方法可以定义为公用的工具方法。

    Double dValue = …;  
    if(dValue> -0.00001 && dValue< 0.0001) { // 约等于0          // do  something  
    } **else** {   
    //  do others  
    }  
    
  • int类型的数据作除法时要注意,得到的有可能不是想要的结果。

int iFz= 2;
int iFm = 3; // 可能为0
// 错误的做法
Double present = iFz / iFm; // 实际得到的是0
*********************
int iFz= 2;
int iFm = 3; // 可能为0
// 正确的做法
if(iFm != 0) {
   Present = (double)iFz/iFm; // 做强制类型转换
} else {
   Present = 100;
}

  • 字符串判断时,空串和空格串的差异需要注意,apache common包中提供了相关的判断方法。
String str="";
if (str != null &&str.length() != 0){
    //do sth
}
=====================================================================
String str="";
if (StringUtils.isNotBlank(str)){
    //do sth
}

  • Equals方法的使用
    • 字符串比较应当使用equals方法。
    • 应该用常量字符串在前,比较变量,避免null错误。
String str_temp="temp";
if(str_temp=="test"){
    //do sth
}
******************
//正确的方法
String str_temp="temp";
if("test".equals(str_temp)){
    //do sth
}

  • 日期处理时,增加一个月,有代码实现时采用了加30天的做法。应该使用java8里的日期处理类

  • 不应当使用字符串拼接的方式,应该用StringBuffer或者StringBuilder代替

  • 多个数据库操作,没有使用事务(编程式事务、声明式事务)

  • 当发生异常时,一般先回滚事务,然后记录日志或者再抛异常,回滚事务意味着及早释放锁资源,并且防止记录日志时出异常,导致无法回滚。

try {
    //do sth 
    commit(status);
} catch (Exception e) {
    rollback(status);//首先回滚事务,释放锁资源
    LOG.error("处理失败:", e);//再记录日志
}
  • 关于分页列表count统计的处理,有很多不合理的代码。

    • Count方法中,用list.size()返回记录条数,这样会把所有的列表数据加载到内存,浪费资源。正确做法是用select count(*)来获取记录条数。

    • 记录日志时避免进行正常业务逻辑的处理,如以下debug操作进行了一次数据库查询,统计记录数量。

    //debug时执行了一次昂贵的数据库交互
    logger.debug(faDao.getFaxxList(kssj,jssj).size ());
    //正确的做法,应该复用之前业务逻辑的查询结果,不要再次执行查询
    int count=faDao.getFaxxList(kssj,jssj).size ();
    
    //do sth use count
    //需要先判断日志级别,不然会有性能消耗,因为日志级别只影响日志输出,但是内容都会计算出来,例如日志里有tostring方法,不管日志级别是什么,都会运行,只是不一定显示
    if(logger.isDebug()){
        logger.debug(count);
    }
    

    说明首先,类似代码不应该提交到svn;其次,如果debug中的代码确实需要执行,则可以增加一个if(logger.isDebug)判断,根据调试级别,避免无效执行。

  • 删除数据库中的一条记录,常见有先使用hibernate的get方法,获取一个bean对象,然后在调用hibernate的delete方法进行删除,这样会有两个数据库操作。实际上直接根据主键删除对象即可,不用先把对象查出来。

  • 数据库查询时,sql中where条件左侧尽量不要使用函数(包括日期函数),以避免降低查询效率。

    Select * from tab where d_larq+3=?;//将不会使用索引
    Select * from tab where trunc(d_larq)=?;//将不会使用索引
    Select * from tab where to_number(C_BH)=?//将不会使用索引
    Select * from tab where c_bh||’01’=?//不会使用索引
    
  • Select *的使用,程序中不建议类似方式,需要明确写出要查询的列,避免列太多。

  • DAO类中,插入数据到数据库,有hibernate的bean,但是仍然使用sql进行插入。应该用hibernate的insert方法,把bean传入即可。

  • 推荐用Integer.valueOf(int)创建Integer对象(其他封装类类似),该方法有可能通过缓存经常请求的值而显著提高空间和时间性能。

  • 不要重复实现apache common包中很多工具类方法。

  • 分页和全部查询可以写一个方法

  • 不应当有空的异常处理块。同时异常信息应该使用日志框架进行记录,不应当调用printStackTrace()方法。该问题在技术备忘录中有要求。

  • 部分try{}catch(..){} 进行了后台日志报错,但是没有给出前台错误提示,这样可能导致用户在已经报错的操作上进行下一步操作,从而引发更严重问题。如果确实不需要提示用户,则可以再注释中进行明确说明。

  • Try代码块不必一味的缩小,相关的业务在一个try中,可确保一个失败而后面的不做。

  • 定时任务不能抛出异常,因为没有程序去管理。定时任务需要自己处理所有的异常信息,并根据异常的情况确定后续的处理流程。可以忽略、重做、发通知等等。

  • 异常的处理最好区分捕获,而不是使用一个try.catch捕获所有的异常

  • List的判空,建议用isEmpty方法,而不要用size()==0

  • 集合类的遍历,建议采用迭代器进行。这是目前比较高效、快捷的。下面列举几种常见的迭代处理代码

    Map<String, String> map = new HashMap<String, String>();
    for (Entry<String, String> entry : map.entrySet()) {
    // 遍历Map
    entry.getKey();
    entry.getValue();
    }
    
    List<String> list = new ArrayList<String>();
    for (String item : list) {
      // 遍历List、Set
    }
    
    Properties props = new Properties();
    for (Entry<Object, Object> entry : props.entrySet()) {
      // 遍历Propties
    }
    
    String[] arrString = new String[0];
    for (String item : arrString) {
      // 遍历数组
    
  • 打开文件后,没有关闭,导致文件占用。
    • 未使用finally处理,导致异常情况下占用资源未释放。

    • 使用了finally处理,多个资源,但是在释放前面资源时发生了异常,导致后面的资源释放失败

  • Dao中使用了getSession,但是没有调用releaseSession,导致数据库链接不释放,应该使用hibernateTemplate提供的方法进行数据库操作,一般情况下不需要getSession。

  • 做文件操作时,直接把文件读入内存,然后进行相应的业务操作,当文件较大,或者线程较多时,会造成内存溢出,导致服务器宕机

    • 压缩程序,接受文件参数,用byte[]数组进行操作,容易造成内存溢出。

    • 应用程序提供文件下载,如果文件较大,线程较多时也会造成内存溢出。

  • SimpleDateFormat有线程安全问题,使用的时候注意线程安全问题,尽量不要使用全局的SimpleDateFormat对象

  • 单例对象的实现,代码中要进行同步的处理,否则有可能创建多个对象。同时对于单例对象的状态、全局属性的修改方法,也要增加synchronized关键字,避免线程安全问题

  • 异常处理中,捕获异常后,没有使用日志框架记录日志的

    日志

  • 记录日志时应该避免出现NullPointException。

  • 每个方法起始应该打印入参数信息,对于对象类型可以转成Json字符串,对于不多的参数用占位符方式输出。

  • 记录trace/debug/info 级别的日志时应使用{}占位符,避免字符串连接减少String对象(不可变)带来的内存开销。

  • 日志内容应该易读、清晰、可描述。要给出当前操作做了什么, 使用的什么数据。

  • 不要在日志中记录密码和个人隐私信息。

  • 记录日志的时候,不要查询数据库。

  • 在for循环中输出日志要谨慎,避免输出大量无用日志。

  • 对于ERROR级别或者WARN级别的日志需要打印异常时相关的参数信息。

  • 应该避免记录日志后有抛出异常,保证日志只记录一次。

  • 代码中不应该出现System print(包括System.out.println和System.error.println)语句

  • 出现异常时应该输出Exceptions的全部Throwable信息,因为logger.error(msg)和logger.error(msg,e.getMessage())这样的日志输出方法会丢失掉最重要的StackTrace信息

    SQL

  • 在循环中(for、wihle),存在查询或更新数据库的脚本,将导致产生大量SQL的问题

  • 在循环中(for、wihle),存在查询或更新数据库的脚本,且没有对垃圾数据进行处理,导致无限循环或一个跨度较大的循环,将导致产生大量SQL的问题

  • 在一次业务处理中,反复从数据库装载同一对象,将导致产生重复大量SQL的问题。

  • SQL脚本操作一批数据,是通过in来实现的,没有预估到in的个数量,导致拼接的SQL很长

  • SQL查询中,不能使用select * 进行查询

  • SQL查询时,条件语句中带or的话,很容易造成全表扫描。最好不要出现这样的sql,可以使用in、union、uion all 代替

作者:Yuyy
博客:https://yuyy.info
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇