(一) 命名風格
1.類名使用駝峰式,但是以下情形例外:DO / BO / DTO / VO / AO / PO / UID等。
- 正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
- 反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
2.方法名情妖、參數(shù)名嗜湃、成員變量、局部變量都統(tǒng)一使用 >- lowerCamelCase 風格扯罐,必須遵從 駝峰形式。
- 正例: localValue / getHttpMessage() / inputUserId
3.常量命名全部大寫,單詞間用下劃線隔開熬荆,力求語義表達完整清楚,不要嫌名字長绸狐。 正例:MAX_STOCK_COUNT
- 反例:MAX_COUNT
4.抽象類命名使用 Abstract 或 Base 開頭;異常類命名使用 Exception 結(jié)尾;測試類 命名以它要測試的類的名稱開始卤恳,以 Test 結(jié)尾累盗。
5. POJO
否則部分框架解析會引起序列化錯誤若债。
- 反例:定義為基本數(shù)據(jù)類型Boolean isDeleted的屬性,它的方法也是isDeleted()
RPC 框架在反向解析的時候拆融,“誤以為”對應(yīng)的屬性名稱是 deleted蠢琳,導(dǎo)致屬性獲取不到,進而拋
出異常镜豹。
6. 包名統(tǒng)一使用小寫傲须,點分隔符之間有且僅有一個自然語義的英語單詞。包名統(tǒng)一使用
趟脂,但是類名如果有復(fù)數(shù)含義泰讽,類名可以使用復(fù)數(shù)形式。
- 正例:應(yīng)用工具類包名為 com.alibaba.ai.util昔期、類名為 MessageUtils(此規(guī)則參考 spring 的框架結(jié)構(gòu))
7.接口類中的方法和屬性不要加任何修飾符號(public 也不要加)已卸,保持代碼的簡潔 性,并加上有效的 Javadoc 注釋硼一。盡量不要在接口里定義變量累澡,如果一定要定義變量,肯定是 與接口方法相關(guān)欠动,并且是整個應(yīng)用的基礎(chǔ)常量永乌。
- 正例:接口方法簽名void commit();
接口基礎(chǔ)常量String COMPANY = "alibaba";- 反例:接口方法定義public abstract void f();
說明:JDK8 中接口允許有默認實現(xiàn),那么這個 default 方法具伍,是對所有實現(xiàn)類都有價值的默 認實現(xiàn)翅雏。
8.舉類名建議帶上 Enum 后綴,枚舉成員名稱需要全大寫人芽,單詞間用下劃線隔開望几。
- 說明:枚舉其實就是特殊的類,域成員均為常量萤厅,且構(gòu)造方法被默認強制是私有橄抹。 正例:枚舉名字為ProcessStatusEnum的成員名稱:SUCCESS / UNKNOWN_REASON。
(二) 常量定義
1. 在 long 或者 Long 賦值時惕味,數(shù)值后使用大寫的 L楼誓,不能是小寫的 l,小寫容易跟數(shù)字 1 混淆名挥,造成誤解疟羹。
- 說明:Long a = 2l; 寫的是數(shù)字的21,還是Long型的2?
2. 不要使用一個常量類維護所有常量,要按常量功能進行歸類榄融,分開維護参淫。 說明:大而全的常量類,雜亂無章愧杯,使用查找功能才能定位到修改的常量涎才,不利于理解和維護。
- 正例:緩存相關(guān)常量放在類 CacheConsts 下;系統(tǒng)配置相關(guān)常量放在類 ConfigConsts 下力九。
(三) 代碼格式
1.【強制】左小括號和字符之間不出現(xiàn)空格;同樣耍铜,右小括號和字符之間也不出現(xiàn)空格;而左大 括號前需要空格。
- 反例:if (空格a == b空格)
2. 【強制】if/for/while/switch/do 等保留字與括號之間都必須加空格畏邢。
3. 【強制】任何二目业扒、三目運算符的左右兩邊都需要加一個空格检吆。
- 說明:運算符包括賦值運算符=舒萎、邏輯運算符&&、加減乘除符號等蹭沛。
4 . 【強制】采用 4 個空格縮進臂寝,禁止使用 tab 字符。
- 說明:如果使用 tab 縮進摊灭,必須設(shè)置 1 個 tab 為 4 個空格咆贬。IDEA 設(shè)置 tab 為 4 個空格時, 請勿勾選Use tab character
而在 eclipse 中帚呼,必須勾選insert spaces for tabs掏缎。
正例: (涉及1-5點)
public static void main(String[] args) {
// 縮進 4 個空格
String say = "hello";
// 運算符的左右必須有一個空格
int flag = 0;
// 關(guān)鍵詞 if 與括號之間必須有一個空格,括號內(nèi)的 f 與左括 號煤杀,0 與右括號不需要空格
if (flag == 0) {
System.out.println(say);
}
// 左大括號前加空格且不換行;左大括號后換行
if (flag == 1) {
System.out.println("world");
// 右大括號前換行眷蜈,右大括號后有 else,不用換行
} else {
System.out.println("ok");
// 在右大括號后直接結(jié)束沈自,則必須換行
}
5. 【強制】注釋的雙斜線與注釋內(nèi)容之間有且僅有一個空格酌儒。
- 正例: // 這是一個注釋,請注意與雙斜杠后面有一個空格
6. 【強制】方法參數(shù)在定義和傳入時枯途,多個參數(shù)逗號后邊必須加空格忌怎。
- 正例:下例中實參的 args1,后邊必須要有一個空格酪夷。 method(args1, args2, args3);
7. 【推薦】單個方法的總行數(shù)不超過 80 行榴啸。
- 說明:包括方法簽名、結(jié)束右大括號晚岭、方法內(nèi)代碼鸥印、注釋、空行、回車及任何不可見字符的總 行數(shù)不超過 80 行辅甥。
- 正例:代碼邏輯分清紅花和綠葉酝润,個性和共性,綠葉邏輯單獨出來成為額外方法璃弄,使主干代碼 更加清晰;共性邏輯抽取成為共性方法要销,便于復(fù)用和維護。
8. 【推薦】不同邏輯夏块、不同語義疏咐、不同業(yè)務(wù)的代碼之間插入一個空行分隔開來以提升可讀性。 說明:任何情形脐供,沒有必要插入多個空行進行隔開浑塞。
(四) OOP 規(guī)約
1. 【強制】避免通過一個類的對象引用訪問此類的靜態(tài)變量或靜態(tài)方法,無謂增加編譯器解析成 本政己,直接用類名來訪問即可酌壕。
2. 【強制】Object 的 equals 方法容易拋空指針異常,應(yīng)使用常量或確定有值的對象來調(diào)用 equals歇由。
- 正例:"test".equals(object);
- 反例:object.equals("test");
說明:推薦使用 java.util.Objects#equals(JDK7 引入的工具類)
3. 【強制】所有的相同類型的包裝類對象之間值的比較卵牍,全部使用 equals 方法比較。
- 說明:對于 Integer var = ? 在-128 至 127 范圍內(nèi)的賦值沦泌,Integer 對象是在 IntegerCache.cache 產(chǎn)生糊昙,會復(fù)用已有對象,這個區(qū)間內(nèi)的 Integer 值可以直接使用==進行 判斷谢谦,但是這個區(qū)間之外的所有數(shù)據(jù)释牺,都會在堆上產(chǎn)生,并不會復(fù)用已有對象回挽,這是一個大坑没咙, 推薦使用 equals 方法進行判斷。
4. 【強制】構(gòu)造方法里面禁止加入任何業(yè)務(wù)邏輯厅各,如果有初始化邏輯镜撩,請放在 init 方法中。
5. 【推薦】使用索引訪問用 String 的 split 方法得到的數(shù)組時队塘,需做最后一個分隔符后有無內(nèi)容的檢查袁梗,否則會有拋 IndexOutOfBoundsException 的風險。
- 說明:
String str = "a,b,c,,";
String[] ary = str.split(","); // 預(yù)期大于 3憔古,結(jié)果是 3 System.out.println(ary.length);
6. 【推薦】當一個類有多個構(gòu)造方法遮怜,或者多個同名方法,這些方法應(yīng)該按順序放置在一起鸿市, 便于閱讀.
7. 【推薦】循環(huán)體內(nèi)锯梁,字符串的連接方式即碗,使用 StringBuilder 的 append 方法進行擴展。
- 說明:下例中陌凳,反編譯出的字節(jié)碼文件顯示每次循環(huán)都會 new 出一個 StringBuilder 對象剥懒, 然后進行 append 操作,最后通過 toString 方法返回 String 對象合敦,造成內(nèi)存資源浪費初橘。
反例:
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello"; }
8. 【推薦】類成員與方法訪問控制從嚴:
- 如果不允許外部直接通過new來創(chuàng)建對象,那么構(gòu)造方法必須是private充岛。
- 工具類不允許有public或default構(gòu)造方法保檐。
- 類非static成員變量并且與子類共享,必須是protected崔梗。
- 類非static成員變量并且僅在本類使用夜只,必須是private。
- 類static成員變量如果僅在本類使用蒜魄,必須是private扔亥。
- 若是static成員變量,考慮是否為final权悟。
- 類成員方法只供類內(nèi)部調(diào)用砸王,必須是private。
- 類成員方法只對繼承類公開峦阁,那么限制為protected。
- 說明:任何類耘成、方法榔昔、參數(shù)、變量瘪菌,嚴控訪問范圍撒会。過于寬泛的訪問范圍,不利于模塊解耦师妙。 思考:如果是一個 private 的方法诵肛,想刪除就刪除,可是一個 public 的 service 成員方法或 成員變量默穴,刪除一下怔檩,不得手心冒點汗嗎?變量像自己的小孩,盡量在自己的視線內(nèi)蓄诽,變量作 用域太大薛训,無限制的到處跑,那么你會擔心的仑氛。
(五) 集合處理
1. 【強制】關(guān)于 hashCode 和 equals 的處理乙埃,遵循如下規(guī)則:
- 只要重寫equals闸英,就必須重寫hashCode。
- 因為Set存儲的是不重復(fù)的對象介袜,依據(jù)hashCode和equals進行判斷甫何,所以Set存儲的 對象必須重寫這兩個方法。
- 如果自定義對象作為Map的鍵遇伞,那么必須重寫hashCode和equals沛豌。
- 說明:String 重寫了 hashCode 和 equals 方法,所以我們可以非常愉快地使用 String 對象 作為 key 來使用赃额。(有面試問)
2.【強制】使用集合轉(zhuǎn)數(shù)組的方法加派,必須使用集合的toArray(T[] array),傳入的是類型完全一樣的數(shù)組跳芳,大小就是 list.size()芍锦。
- 說明:使用 toArray 帶參方法,入?yún)⒎峙涞臄?shù)組空間不夠大時飞盆,toArray 方法內(nèi)部將重新分配內(nèi)存空間娄琉,并返回新數(shù)組地址;如果數(shù)組元素個數(shù)大于實際所需,下標為[ list.size() ]的數(shù)組元素將被置為 null吓歇,其它數(shù)組元素保持原值孽水,因此最好將方法入?yún)?shù)組大小定義與集合元素個數(shù)一致。
- 正例:
List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);- 反例:直接使用 toArray 無參方法存在問題城看,此方法返回值只能是 Object[]類女气,若強轉(zhuǎn)其它 類型數(shù)組將出現(xiàn) ClassCastException 錯誤。
3. 【強制】使用工具類 Arrays.asList()把數(shù)組轉(zhuǎn)換成集合時测柠,不能使用其修改集合相關(guān)的方 法炼鞠,它的 add/remove/clear 方法會拋出 UnsupportedOperationException 異常。
- 說明:asList 的返回對象是一個 Arrays 內(nèi)部類轰胁,并沒有實現(xiàn)集合的修改方法谒主。Arrays.asList 體現(xiàn)的是適配器模式,只是轉(zhuǎn)換接口赃阀,后臺的數(shù)據(jù)仍是數(shù)組霎肯。
String[] str = new String[] { "you", "wu" };
List list = Arrays.asList(str);- 第一種情況:list.add("yangguanbao"); 運行時異常。
- 第二種情況:str[0] = "gujin"; 那么list.get(0)也會隨之修改榛斯。
4. 【強制】不要在 foreach 循環(huán)里進行元素的 remove/add 操作观游。remove 元素請使用 Iterator方式,如果并發(fā)操作肖抱,需要對 Iterator 對象加鎖备典。
正例:
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (刪除元素的條件) {
iterator.remove();
}
}
反例:
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
5. 【推薦】集合初始化時,指定集合初始值大小意述。
- 說明:HashMap使用HashMap(int initialCapacity) 初始化提佣。
- 正例:initialCapacity = (需要存儲的元素個數(shù) / 負載因子) + 1吮蛹。注意負載因子(即 loader factor)默認為 0.75,如果暫時無法確定初始值大小拌屏,請設(shè)置為 16(即默認值)潮针。
- 反例:HashMap 需要放置 1024 個元素,由于沒有設(shè)置容量初始大小倚喂,隨著元素不斷增加每篷,容 量 7 次被迫擴大,resize 需要重建 hash 表端圈,嚴重影響性能焦读。
6. 【推薦】使用 entrySet 遍歷 Map 類集合 KV,而不是 keySet 方式進行遍歷舱权。
- 說明:keySet 其實是遍歷了 2 次矗晃,一次是轉(zhuǎn)為 Iterator 對象,另一次是從 hashMap 中取出 key 所對應(yīng)的 value宴倍。而 entrySet 只是遍歷了一次就把 key 和 value 都放到了 entry 中张症,效 率更高。如果是 JDK8鸵贬,使用 Map.foreach 方法俗他。
- 正例:values()返回的是 V 值集合,是一個 list 集合對象;keySet()返回的是 K 值集合阔逼,是 一個 Set 集合對象;entrySet()返回的是 K-V 值組合集合兆衅。
7. 【推薦】高度注意 Map 類集合 K/V 能不能存儲 null 值的情況,如下表格:
image.png
反例: 由于 HashMap 的干擾颜价,很多人認為 ConcurrentHashMap 是可以置入 null 值涯保,而事實上, 存儲 null 值時會拋出 NPE 異常周伦。
8. 【參考】利用 Set 元素唯一的特性,可以快速對一個集合進行去重操作未荒,避免使用 List 的 contains 方法進行遍歷专挪、對比、去重操作片排。
(六) 并發(fā)處理
1. 【強制】線程資源必須通過線程池提供寨腔,不允許在應(yīng)用中自行顯式創(chuàng)建線程。
- 說明:使用線程池的好處是減少在創(chuàng)建和銷毀線程上所消耗的時間以及系統(tǒng)資源的開銷率寡,解決 資源不足的問題迫卢。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致消耗完內(nèi)存或 者“過度切換”的問題冶共。
2. 【強制】線程池不允許使用 Executors 去創(chuàng)建乾蛤,而是通過 ThreadPoolExecutor 的方式每界,這樣的處理方式讓寫的同學(xué)更加明確線程池的運行規(guī)則,規(guī)避資源耗盡的風險家卖。
- 說明:Executors 返回的線程池對象的弊端如下:
- 1)FixedThreadPool 和 SingleThreadPool:
允許的請求隊列長度為 Integer.MAX_VALUE眨层,可能會堆積大量的請求,從而導(dǎo)致 OOM上荡。- 2)CachedThreadPool 和 ScheduledThreadPool:
允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE趴樱,可能會創(chuàng)建大量的線程,從而導(dǎo)致 OOM酪捡。
(七) 控制語句
1. 【強制】在一個 switch 塊內(nèi)叁征,每個 case 要么通過 break/return 等來終止,要么注釋說明程 序?qū)⒗^續(xù)執(zhí)行到哪一個 case 為止;在一個 switch 塊內(nèi)逛薇,都必須包含一個 default 語句并且 放在最后捺疼,即使空代碼。
2. 【強制】在 if/else/for/while/do 語句中必須使用大括號金刁。即使只有一行代碼帅涂,避免采用 單行的編碼方式:if (condition) statements;
3. 【推薦】除常用方法(如 getXxx/isXxx)等外,不要在條件判斷中執(zhí)行其它復(fù)雜的語句尤蛮,將 復(fù)雜邏輯判斷的結(jié)果賦值給一個有意義的布爾變量名媳友,以提高可讀性。
- 說明:很多 if 語句內(nèi)的邏輯相當復(fù)雜产捞,閱讀者需要分析條件表達式的最終結(jié)果醇锚,才能明確什么 樣的條件執(zhí)行什么樣的語句,那么坯临,如果閱讀者分析邏輯表達式錯誤呢?
- 正例:
// 偽代碼如下
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {
... }- 反例:
if ((file.open(fileName, "w") != null) && (...) || (...)) { ...
}
(八) 注釋規(guī)約
1. 【強制】類焊唬、類屬性、類方法的注釋必須使用 Javadoc 規(guī)范看靠,使用/*內(nèi)容/格式赶促,不得使用 // xxx方式。
- 說明:在 IDE 編輯窗口中挟炬,Javadoc 方式會提示相關(guān)注釋鸥滨,生成 Javadoc 可以正確輸出相應(yīng)注 釋;在 IDE 中,工程調(diào)用方法時谤祖,不進入方法即可懸浮提示方法婿滓、參數(shù)、返回值的意義粥喜,提高 閱讀效率凸主。
2. 【強制】所有的抽象方法(包括接口中的方法)必須要用 Javadoc 注釋、除了返回值额湘、參數(shù)卿吐、 異常說明外旁舰,還必須指出該方法做什么事情,實現(xiàn)什么功能但两。
- 說明:對子類的實現(xiàn)要求鬓梅,或者調(diào)用注意事項,請一并說明谨湘。
3. 【強制】方法內(nèi)部單行注釋绽快,在被注釋語句上方另起一行,使用//注釋紧阔。方法內(nèi)部多行注釋使用/* */注釋坊罢,注意與代碼對齊。
4. 【參考】謹慎注釋掉代碼擅耽。在上方詳細說明活孩,而不是簡單地注釋掉。如果無用乖仇,則刪除憾儒。
- 說明:代碼被注釋掉有兩種可能性:
- 1)后續(xù)會恢復(fù)此段代碼邏輯。
- 2)永久不用乃沙。前者如果沒 有備注信息起趾,難以知曉注釋動機。后者建議直接刪掉(代碼倉庫保存了歷史代碼)
5. 【參考】特殊注釋標記警儒,請注明標記人與標記時間训裆。注意及時處理這些標記,通過標記掃描蜀铲, 經(jīng)常清理此類標記边琉。線上故障有時候就是來源于這些標記處的代碼。
- 待辦事宜(TODO):( 標記人记劝,標記時間变姨,[預(yù)計處理時間]) 表示需要實現(xiàn),但目前還未實現(xiàn)的功能厌丑。這實際上是一個 Javadoc 的標簽钳恕,目前的 Javadoc還沒有實現(xiàn),但已經(jīng)被廣泛使用蹄衷。只能應(yīng)用于類,接口和方法(因為它是一個 Javadoc 標簽)厘肮。
- 錯誤愧口,不能工作(FIXME):(標記人,標記時間类茂,[預(yù)計處理時間])在注釋中用 FIXME 標記某代碼是錯誤的耍属,而且不能工作托嚣,需要及時糾正的情況