码迷,mamicode.com
首页 > 其他好文 > 详细

代码规范实践

时间:2018-09-19 11:37:01      阅读:156      评论:0      收藏:0      [点我收藏+]

标签:extern   乐观锁   构造方法   英文   inf   公有   thread   nta   并发   

规范等级说明

  • 级别I:   默认级别,要求所有项目必须遵守。
  • 级别II:  建议所有项目中遵守。
  • 级别III: 鼓励各个项目根据实际情况执行。

1.格式与命名规范(Formating and Naming Conventions)

1.1  缩进

  使用Tab缩进。(I)

1.2 换行

   每行80字符。(I)

   if,for,while语句只有单句时,如果该句可能引起阅读混淆,需要用" {"和"}"括起来,否则可以省略。

//错误,需要使用花括号{}括起来
if (condition)
if(condition) doSomething();
else
doSomething();

 

1.3 命名规则 

  • 不允许使用汉语拼音命名 (I)
  • 代码命名不能以下划线或美元符号开始或结束,反例: _name / __name / $Object / name_ / name$ / Object$   (I)
  • 命令必须用驼峰形式 (I)
  • 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长;(I)

  • 遇到缩写如XML时,仅首字母大写,即loadXmlDocument()而不是loadXMLDocument()
  • Package名必须全部小写,尽量使用单个单词;(I)
  • Interface名可以是一个名词或形容词(加上‘able‘,‘ible‘, or ‘er‘后缀),如Runnable,Accessible。
    为了基于接口编程,不采用首字母为I或加上IF后缀的命名方式,如IBookDao,BookDaoIF。
  • 页面部件名建议命名为:btnOK、lblName或okBtn、nameLbl。(II)
    其中btn、lbl缩写代表按钮(Button)、标签(Label)。
  • 局部变量及输入参数不要与类成员变量同名(get/set方法与构造函数除外)
  • 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾 (I)
  • 枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开;(I)

  • 各层命名规约:
    A) Service/DAO层方法命名规约 (I)

    1) 获取单个对象的方法用findBy做前缀。(I)
    2) 获取多个对象的方法用list做前缀,
    3) 如果是查询用search做前缀。
    4) 获取统计值的方法用count做前缀。
    5) 插入的方法用save(推荐)或insert做前缀。 
    6) 删除的方法用remove(推荐)或delete做前缀。 
    7) 修改的方法用update做前缀。

    B) 领域模型命名规约 (I)
    1) 数据对象:xxx,遵循驼峰,xxx即为数据表名,例如 User。
    2) 数据传输对象:xxxDTO,xxx为业务领域相关的名称。 
    3) 展示对象:xxxVO,xxx一般为网页名称。
    4) POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。 

     

  • 常量定义

    • 常量必须先定义(I)

       

1.4 声明

  • 修饰符应该按照如下顺序排列:public, protected, private, abstract, static, final, transient, volatile, synchronized, native, strictfp。
  • 类与接口的声明顺序(可用Eclipse的source->sort members功能自动排列): 
    1. 静态成员变量 / Static Fields
    2. 静态初始化块 / Static Initializers
    3. 成员变量 / Fields
    4. 初始化块 / Initializers
    5. 构造器 / Constructors
    6. 静态成员方法 / Static Methods
    7. 成员方法 / Methods
    8. 重载自Object的方法如toString(), hashCode() 和main方法
    9. 类型(内部类) / Types(Inner Classes)

     同等的类型,按public, protected, private的顺序排列。

1.5 格式

  • IDE的text file encoding设置为UTF-8; IDE中文件的换行符使用Unix格式, 不要使用 windows 格式 (I)

2.注释规范(Document Convertions)

2.1 注释类型

2.1.1 JavaDoc注释

  略。

2.1.2 失效代码注释

  由/*...*/界定,标准的C-Style的注释。专用于注释已失效的代码。

/*
 * Comment out the code
 * String s = "hello";
 * System.out.println(s);
 */

2.1.3 代码细节注释

  由//界定,专用于注释代码细节,即使有多行注释也仍然使用//,以便与用/**/注释的失效代码分开

  除了私有变量外,不推荐使用行末注释。

class MyClass {
    private int myField; // An end-line comment.

    public void myMethod {
        //a very very long
       //comment.
       if (condition1) {
       //condition1 comment
          ...
        } else {
        //elses condition comment
          ...
        }
    }
}          

 

2.2 注释的格式

  • 注释中的第一个句子要以(英文)句号、问号或者感叹号结束。Javadoc生成工具会将注释中的第一个句子放在方法汇总表和索引中。
  • 为了在JavaDoc和IDE中能快速链接跳转到相关联的类与方法,尽量多的使用@see xxx.MyClass,@see xx.MyClass#find(String)。
  • Class必须以@author 作者名声明作者,不需要声明@version与@date,由版本管理系统保留此信息。(II)
  • 如果注释中有超过一个段落,用<p>分隔。(II)
  • 示例代码以<pre></pre>包裹。(II)
  • 标识(java keyword, class/method/field/argument名,Constants) 以<code></code>包裹。(II)
  • 标识在第一次出现时以{@linkxxx.Myclass}注解以便JavaDoc与IDE中可以链接。(II)

2.3 注释的内容

2.3.1 可精简的注释内容

    注释中的每一个单词都要有其不可缺少的意义,注释里不写"@param name -名字"这样的废话。
    如果该注释是废话,连同标签删掉它,而不是自动生成一堆空的标签,如空的@param name,空的@return。

2.3.2 推荐的注释内容

  • 对于API函数如果存在契约,必须写明它的前置条件(precondition),后置条件(postcondition),及不变式(invariant)。(II)
  • 对于调用复杂的API尽量提供代码示例。(II)
  • 对于已知的Bug需要声明。(II)
  • 在本函数中抛出的unchecked exception尽量用@throws说明。(II)
  • 类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用 //xxx 方式 
  • 所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能 
  •  所有的类都必须添加创建者信息 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐 
  •  所有的枚举类型字段必须要有注释,说明每个数据项的用途 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑 等的修改 

     

2.3.3 Null规约

   如果方法允许Null作为参数,或者允许返回值为Null,必须在JavaDoc中说明。
   如果没有说明,方法的调用者不允许使用Null作为参数,并认为返回值是Null Safe的。

/**
 * 获取对象.
 *
 * @ return the object to found or null if not found.
 */
Object get(Integer id){
...
}

 

2.3.4 特殊代码注释

  • 代码质量不好但能正常运行,或者还没有实现的代码用//TODO: 或 //XXX:声明 
  • 存在错误隐患的代码用//FIXME:声明

3.编程规范(Programming Conventions)

3.1基本规范

  1. 当面对不可知的调用者时,方法需要对输入参数进行校验,如不符合抛出IllegalArgumentException,建议使用Spring的Assert系列函数。 (II)
  2. 隐藏工具类的构造器,确保只有static方法和变量的类不能被构造实例。
  3. 变量,参数和返回值定义尽量基于接口而不是具体实现类,如Map map = new HashMap();Map map = Maps.newHashMap();
  4. 代码中不能使用System.out.println(),e.printStackTrace(),必须使用logger打印信息。(I)

3.2 异常处理

  1. 重新抛出的异常必须保留原来的异常,即throw new NewException("message", e); 而不能写成throw new NewException("message")。(I)
  2. 在所有异常被捕获且没有重新抛出的地方必须写日志。 (I)
  3. 如果属于正常异常的空异常处理块必须注释说明原因,否则不允许空的catch块。(I)
  4. 框架尽量捕获低级异常,并封装成高级异常重新抛出,隐藏低级异常的细节。(III)
  5. 不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如: IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查 来规避,保证程序健壮性
  6.  异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请 将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的 内容 
  7. 有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回 滚事务 
  8. finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch 
  9. 不能在 finally 块中使用 return,finally 块中的 return 返回后方法结束执行,不 会再执行 try 块中的 return 语句 
  10. 方法的返回值可以为 null,不强制返回空集合,或者空对象等,必须添加注释充分 说明什么情况下会返回 null 值。调用方需要进行 null 判断防止 NPE 问题 (I)
  11. 防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:
    1) 返回类型为包装数据类型,有可能是null,返回int值时注意判空。

    反例:public int f(){ return Integer 对象}; 如果为 null,自动解箱抛 NPE。 
    2) 数据库的查询结果可能为null。
    3) 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
    4) 远程调用返回对象,一律要求进行NPE判断。

    5) 对于Session中获取的数据,建议NPE检查,避免空指针。
    6) 级联调用obj.getA().getB().getC();一连串调用,易产生NPE。 

3.3 JDK

  1. 重载方法必须使用@Override,可避免父类方法改变时导致重载函数失效。
  2. 不需要关心的warning信息用@SuppressWarnings("unused"), @SuppressWarnings("unchecked"), @SuppressWarnings("serial") 注释。

3.4 日志

  1. 应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架(I)

    SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    private static final Logger logger = LoggerFactory.getLogger(Abc.class); 

  2. 对 trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式,(I)

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

  3. 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么往上 抛。正例:logger.error(各类参数或者对象toString + "_" + e.getMessage(), e); (I)

4.OOP规范

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

  • 所有的覆写方法,必须加@Override 注解(I)

  • 不能使用过时的类或方法

  • Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals ,正例: "test".equals(object); 反例: object.equals("test");  (I) 说明:推荐使用java.util.Objects#equals (JDK7引入的工具类)  

  • 所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较 (I)

  •  定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值 

  •  构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中 (I)

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

  • 当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起, 便于阅读 

  • 类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter方法 

  • 字符串的联接方式,使用 StringBuilder 的 append 方法进行扩展 (I)

  • final 可提高程序响应效率,声明成 final 的情况 ,
    a>不需要重新赋值的变量,包括类属性、局部变量
    b>对象参数前加final,表示不允许修改引用的指向
    c>类方法确定不允许被重写 

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

  • 循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外) 

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

  • 方法中需要进行参数校验的场景,

    1. 调用频次低的方法

    2. 执行时间开销很大的方法,参数校验时间几乎可以忽略不计,但如果因为参数错误导致,中间执行回退,或者错误,那得不偿失 

    3. 需要极高稳定性和可用性的方法 

    4. 对外提供的开放接口,不管是RPC/API/HTTP接口 

    5. 敏感权限入口 

  • 方法中不需要参数校验的场景

    1. 极有可能被循环调用的方法,不建议对参数进行校验。但在方法说明里必须注明外部参数检查

    2. 底层的方法调用频度都比较高,一般不校验。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底层才会暴露问题。一般 DAO 层与 Service 层都在同一个应用中,部署在同一 台服务器中,所以 DAO 的参数校验,可以省略 

    3. 被声明成private只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参 数已经做过检查或者肯定不会有问题,此时可以不校验参数 

  • 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度 

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

  • 获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime() ,

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

  • 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存 

5.集合处理
  • 不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁 (I)

    Iterator<String> it = a.iterator(); 
    while(it.hasNext()){
           String temp = it.next(); 
           if(删除元素的条件){
               it.remove(); 
           } 
    } 

     

     

  • 集合初始化时,尽量指定集合初始值大小,说明:ArrayList尽量使用ArrayList(int initialCapacity) 初始化 

  • 使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历

  • 利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的 contains 方法进行遍历、对比、去重操作 

     

6.并发处理
  • 获取单例对象需要保证线程安全,其中的方法也要保证线程安全,资源驱动类、工具类、单例工厂类都需要注意 

  • 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯 

  • 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程 

    1. 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下:
      1)FixedThreadPool 和 SingleThreadPool:
      允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
      2)CachedThreadPool 和 ScheduledThreadPool:
      允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。 

     

  • 【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为

    static,必须加锁,或者使用 DateUtils 工具类。

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

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

     

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

  • 并发修改同一记录时,避免更新丢失,要么在应用层加锁,要么在缓存加锁,要么在 数据库层使用乐观锁,使用 version 作为更新依据。说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次 数不得小于 3 次。 

  • 多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获 抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题 

  •  

5.自动代码检查

   使用Eclipse技术分享图片与 Inellij IDEA技术分享图片的代码校验功能已经排除了很多问题。

  1. Eclipse:在Windows->Preferences->Java-Compiler->Errors/Warnings中,按本文档将一些原来Ignore的规则打开。
  2. IDEA:在Setting->Errors中设定规则,调用Analyzer->Inspece Code进行校验。

6.其他

uap关于list的遍历

List result = biz.getList();
if(result!=null){
    for(int i=0;i<CheckList.checkListSize(result);i++){
        // TODO
    }
}
addWhere("param=123");
改成
addWhere("param","=","123");

 

 

代码规范实践

标签:extern   乐观锁   构造方法   英文   inf   公有   thread   nta   并发   

原文地址:https://www.cnblogs.com/ephemeral/p/9673371.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!