《阿里巴巴Java规约插件》使用总结

前言

小编用自己的项目做白老鼠,试试这个阿里巴巴荣誉出品的《Java规约插件》


结果居然有4800+条问题!!简直吓得小编怀疑人生有木有!?(默哀0.01秒)小编依旧怀着认真的科学精神进入试验田(快开车,别废话!)。如想了解如何安装插件和插件的使用技巧请猛戳:http://www.jianshu.com/p/834899aa90b4

入题

扫描出三个分类结果(类jira的bug优先级分为:blocker,critical,major,minor,trivial)

  • Blocker 有妨碍的
  • Critical 紧要的
  • Major 严重的

具体的语法问题,值得学习下:

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

  • 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
    说明:不要在方法体内定义:Pattern pattern = Pattern.compile(规则);
    public class XxxClass {
    // Use precompile
    private static Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+");
    public Pattern getNumberPattern() {
    // Avoid use Pattern.compile in method body.
    Pattern localPattern = Pattern.compile("[0-9]+");
    return localPattern;
    }
    }

  • 多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。 //org.apache.commons.lang3.concurrent.BasicThreadFactory
    ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
    new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
    executorService.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
    //do something
    }
    },initialDelay,period, TimeUnit.HOURS);

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

  • 线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
    1)newFixedThreadPool和newSingleThreadExecutor:
      主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
    2)newCachedThreadPool和newScheduledThreadPool:
      主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

Positive example 1:
//org.apache.commons.lang3.concurrent.BasicThreadFactory
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());

Positive example 2:
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();

//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200,
     0L, TimeUnit.MILLISECONDS,
     new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown

Positive example 3:
<bean id="userThreadPool"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="queueCapacity" value="2000" />

<property name="threadFactory" value= threadFactory />
    <property name="rejectedExecutionHandler">
        <ref local="rejectedExecutionHandler" />
    </property>
</bean>
//in code
userThreadPool.execute(thread);
  • 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。

  • Critical 紧要的

Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。

public void f(String str){
    String inner = "hi";
    if(inner.equals(str)){
        System.out.println("hello world");
    }
}
  • SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。 说明:如果是JDK8的应用,可以使用instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。

Positive example 1:
private static final String FORMAT = "yyyy-MM-dd HH:mm:ss";
public String getFormat(Date date){
SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT);
return sdf.format(date);
}

Positive example 2:
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void getFormat(){
synchronized (sdf){
sdf.format(new Date());
….;
}

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

  • 不能使用过时的类或方法。 说明:java.net.URLDecoder 中的方法decode(String encodeStr) 这个方法已经过时,应该使用双参数decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。

  • 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。

    switch( x ){
    case 1 :
    break ;
    case 2 :
    break ;
    default :
    }

  • 常量命名应该全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长

    public class ConstantNameDemo {

    /**

    • max stock count
      */
      public static final Long MAX_STOCK_COUNT = 50000L;
  • 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
    public enum TestEnum {
    /**
    * agree
    /
    agree("agree"),
    /
    *
    * reject
    */
    reject("reject");

      private String action;
    
      TestEnum(String action) {
          this.action = action;
      }
    
      public String getAction() {
          return action;
      }
    

    }

  • 所有编程相关的命名均不能以下划线或美元符号开始

  • 抽象类命名使用Abstract或Base开头

    abstract class BaseControllerDemo{
    }

    abstract class AbstractActionDemo{
    }

  • 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase,必须遵从驼峰形式

  • Major 严重的
  • 不允许任何魔法值(即未经定义的常量)直接出现在代码中。
    Negative example:
    //Magic values, except for predefined, are forbidden in coding.
    if(key.equals("Id#taobao_1")){
    //...
    }
    Positive example:
    String KEY_PRE = "Id#taobao_1";
    if(key.equals(KEY_PRE)){
    //...
    }

  • 中括号是数组类型的一部分,数组定义如下:String[] args

    String[] a = new String[3];

  • 及时清理不再使用的代码段或配置信息。 说明:对于垃圾代码或过时配置,坚决清理干净,避免程序过度臃肿,代码冗余。
    Positive example: For codes which are temporarily removed and likely to be reused, use /// to add a reasonable note.
    public static void hello() {
    /// Business is stopped temporarily by the owner.
    // Business business = new Business();
    // business.active();
    System.out.println("it's finished");
    }

  • 循环体内,字符串的联接方式,使用StringBuilder的append方法进行扩展。 说明:反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源浪费。
    反例:
    String result;
    for (String string : tagNameList) {
    result = result + string;
    }
    正例:
    StringBuilder stringBuilder = new StringBuilder();
    for (String string : tagNameList) {
    stringBuilder.append(string);
    }
    String result = stringBuilder.toString();

  • 所有的抽象方法(包括接口中的方法)必须要用javadoc注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。 说明:如有实现和调用注意事项,请一并说明。
    /**

    • fetch data by rule id
    • @param ruleId rule id
    • @param page page number
    • @param jsonContext json format context
    • @return Result<XxxxDO>
      */
      Result<XxxxDO> fetchDataByRuleId(Long ruleId, Integer page, String jsonContext);
  • 所有的类都必须添加创建者信息。 说明:在设置模板时,注意IDEA的@author为${USER},而eclipse的@author为${user},大小写有区别,而日期的设置统一为yyyy/MM/dd的格式。
    /**

    • Demo class
    • @author keriezhang
    • @date 2016/10/31
      */
      public class CodeNoteDemo {
      }
  • 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释。注意与代码对齐。
    public void method() {
    // Put single line comment above code. (Note: align '//' comment with code)
    int a = 3;

      /**
      * Some description about follow code. (Note: align '/**' comment with code)
      */
      int b = 4;
    

    }

  • 类、类属性、类方法的注释必须使用javadoc规范,使用/内容/格式,不得使用//xxx方式和/xxx/方式。 说明:在IDE编辑窗口中,javadoc方式会提示相关注释,生成javadoc可以正确输出相应注释;在IDE中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
    /
    *

    • XXX class function description.

    /
    public class XxClass implements Serializable {
    private static final long serialVersionUID = 113323427779853001L;
    /
    *
    * id
    /
    private Long id;
    /
    *
    * title
    */
    private String title;

      /**
       * find by id
       * 
       * @param ruleId rule id
       * @param page start from 1
       * @return Result<Xxxx>
       */
      public Result<Xxxx> funcA(Long ruleId, Integer page) {
          return null;
      }
    

    }

  • 类名使用UpperCamelCase风格,必须遵从驼峰形式,但以下情形例外:(领域模型的相关命名)DO / BO / DTO / VO / DAO

  • 除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量,以提高可读性。 说明:很多if语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么,如果阅读者分析逻辑表达式错误呢?

Negative example:
if ((file.open(fileName, "w") != null) && (...) || (...)) {
...
}

Positive example:
boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {
...
}

  • 集合初始化时,指定集合初始值大小。 说明:HashMap使用如下构造方法进行初始化,如果暂时无法确定集合大小,那么指定默认值(16)即可。

Negative example:
Map<String, String> map = new HashMap<String, String>();

Positive example:
Map<String, String> map = new HashMap<String, String>(16);

小结

总的来说,阿里巴巴Java规约插件还是对项目很有帮助的,大家用起来吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容