前言
小編用自己的項(xiàng)目做白老鼠,試試這個(gè)阿里巴巴榮譽(yù)出品的《Java規(guī)約插件》

結(jié)果居然有4800+條問題?。『喼眹樀眯【帒岩扇松心居校??(默哀0.01秒)小編依舊懷著認(rèn)真的科學(xué)精神進(jìn)入試驗(yàn)田(快開車,別廢話?。H缦肓私馊绾伟惭b插件和插件的使用技巧請猛戳:http://www.itdecent.cn/p/834899aa90b4
入題
掃描出三個(gè)分類結(jié)果(類jira的bug優(yōu)先級分為:blocker,critical,major,minor,trivial)
-
Blocker 有妨礙的
-
Critical 緊要的
-
Major 嚴(yán)重的
具體的語法問題,值得學(xué)習(xí)下:
Blocker 有妨礙的
在if/else/for/while/do語句中必須使用大括號,即使只有一行代碼,避免使用下面的形式:if (condition) statements;
在使用正則表達(dá)式時(shí),利用好其預(yù)編譯功能,可以有效加快正則匹配速度。
說明:不要在方法體內(nèi)定義:Pattern pattern = Pattern.compile(規(guī)則);
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;
}
}多線程并行處理定時(shí)任務(wù)時(shí),Timer運(yùn)行多個(gè)TimeTask時(shí),只要其中之一沒有捕獲拋出的異常,其它任務(wù)便會(huì)自動(dòng)終止運(yùn)行,使用ScheduledExecutorService則沒有這個(gè)問題。 //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()的問題。一個(gè)是字母的O,一個(gè)是數(shù)字的0,加@Override可以準(zhǔn)確判斷是否覆蓋成功。另外,如果在抽象類中對方法簽名進(jìn)行修改,其實(shí)現(xiàn)類會(huì)馬上編譯報(bào)錯(cuò)。
線程池不允許使用Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。 說明:Executors各個(gè)方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
??主要問題是堆積的請求處理隊(duì)列可能會(huì)耗費(fèi)非常大的內(nèi)存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
??主要問題是線程數(shù)最大數(shù)是Integer.MAX_VALUE,可能會(huì)創(chuàng)建數(shù)量非常多的線程,甚至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);
避免通過一個(gè)類的對象引用訪問此類的靜態(tài)變量或靜態(tài)方法,無謂增加編譯器解析成本,直接用類名來訪問即可。
-
Critical 緊要的
Object的equals方法容易拋空指針異常,應(yīng)使用常量或確定有值的對象來調(diào)用equals。
public void f(String str){
String inner = "hi";
if(inner.equals(str)){
System.out.println("hello world");
}
}
- SimpleDateFormat 是線程不安全的類,一般不要定義為static變量,如果定義為static,必須加鎖,或者使用DateUtils工具類。 說明:如果是JDK8的應(yīng)用,可以使用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");
}
};
不能使用過時(shí)的類或方法。 說明:java.net.URLDecoder 中的方法decode(String encodeStr) 這個(gè)方法已經(jīng)過時(shí),應(yīng)該使用雙參數(shù)decode(String source, String encode)。接口提供方既然明確是過時(shí)接口,那么有義務(wù)同時(shí)提供新的接口;作為調(diào)用方來說,有義務(wù)去考證過時(shí)方法的新實(shí)現(xiàn)是什么。
-
在一個(gè)switch塊內(nèi),每個(gè)case要么通過break/return等來終止,要么注釋說明程序?qū)⒗^續(xù)執(zhí)行到哪一個(gè)case為止;在一個(gè)switch塊內(nèi),都必須包含一個(gè)default語句并且放在最后,即使它什么代碼也沒有。
switch( x ){
case 1 :
break ;
case 2 :
break ;
default :
} -
常量命名應(yīng)該全部大寫,單詞間用下劃線隔開,力求語義表達(dá)完整清楚,不要嫌名字長
public class ConstantNameDemo {
/**
- max stock count
*/
public static final Long MAX_STOCK_COUNT = 50000L;
- max stock count
-
所有的枚舉類型字段必須要有注釋,說明每個(gè)數(shù)據(jù)項(xiàng)的用途。
public enum TestEnum {
/**
* agree
/
agree("agree"),
/*
* reject
*/
reject("reject");private String action; TestEnum(String action) { this.action = action; } public String getAction() { return action; }}
所有編程相關(guān)的命名均不能以下劃線或美元符號開始
-
抽象類命名使用Abstract或Base開頭
abstract class BaseControllerDemo{
}abstract class AbstractActionDemo{
} 方法名、參數(shù)名、成員變量、局部變量都統(tǒng)一使用lowerCamelCase,必須遵從駝峰形式
-
Major 嚴(yán)重的
不允許任何魔法值(即未經(jīng)定義的常量)直接出現(xiàn)在代碼中。
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)){
//...
}-
中括號是數(shù)組類型的一部分,數(shù)組定義如下:String[] args
String[] a = new String[3];
及時(shí)清理不再使用的代碼段或配置信息。 說明:對于垃圾代碼或過時(shí)配置,堅(jiān)決清理干凈,避免程序過度臃腫,代碼冗余。
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");
}循環(huán)體內(nèi),字符串的聯(lián)接方式,使用StringBuilder的append方法進(jìn)行擴(kuò)展。 說明:反編譯出的字節(jié)碼文件顯示每次循環(huán)都會(huì)new出一個(gè)StringBuilder對象,然后進(jìn)行append操作,最后通過toString方法返回String對象,造成內(nèi)存資源浪費(fèi)。
反例:
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注釋、除了返回值、參數(shù)、異常說明外,還必須指出該方法做什么事情,實(shí)現(xiàn)什么功能。 說明:如有實(shí)現(xiàn)和調(diào)用注意事項(xiàng),請一并說明。
/**- 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);
-
所有的類都必須添加創(chuàng)建者信息。 說明:在設(shè)置模板時(shí),注意IDEA的@author為${USER},而eclipse的@author為${user},大小寫有區(qū)別,而日期的設(shè)置統(tǒng)一為yyyy/MM/dd的格式。
/**- Demo class
- @author keriezhang
- @date 2016/10/31
*/
public class CodeNoteDemo {
}
-
方法內(nèi)部單行注釋,在被注釋語句上方另起一行,使用//注釋。方法內(nèi)部多行注釋使用/* */注釋。注意與代碼對齊。
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規(guī)范,使用/內(nèi)容/格式,不得使用//xxx方式和/xxx/方式。 說明:在IDE編輯窗口中,javadoc方式會(huì)提示相關(guān)注釋,生成javadoc可以正確輸出相應(yīng)注釋;在IDE中,工程調(diào)用方法時(shí),不進(jìn)入方法即可懸浮提示方法、參數(shù)、返回值的意義,提高閱讀效率。
/*- 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風(fēng)格,必須遵從駝峰形式,但以下情形例外:(領(lǐng)域模型的相關(guān)命名)DO / BO / DTO / VO / DAO
除常用方法(如getXxx/isXxx)等外,不要在條件判斷中執(zhí)行復(fù)雜的語句,將復(fù)雜邏輯判斷的結(jié)果賦值給一個(gè)有意義的布爾變量,以提高可讀性。 說明:很多if語句內(nèi)的邏輯相當(dāng)復(fù)雜,閱讀者需要分析條件表達(dá)式的最終結(jié)果,才能明確什么樣的條件執(zhí)行什么樣的語句,那么,如果閱讀者分析邏輯表達(dá)式錯(cuò)誤呢?
Negative example:
if ((file.open(fileName, "w") != null) && (...) || (...)) {
...
}
Positive example:
boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {
...
}
- 集合初始化時(shí),指定集合初始值大小。 說明:HashMap使用如下構(gòu)造方法進(jìn)行初始化,如果暫時(shí)無法確定集合大小,那么指定默認(rèn)值(16)即可。
Negative example:
Map<String, String> map = new HashMap<String, String>();
Positive example:
Map<String, String> map = new HashMap<String, String>(16);
小結(jié)
總的來說,阿里巴巴Java規(guī)約插件還是對項(xiàng)目很有幫助的,大家用起來吧。