新增11條新規(guī)約织盼!阿里Java開(kāi)發(fā)手冊(cè)|黃山版杨何,擁抱規(guī)范酱塔,遠(yuǎn)離傷害

前言

阿里開(kāi)發(fā)手冊(cè)是阿里近萬(wàn)名開(kāi)發(fā)同學(xué)集體智慧的結(jié)晶,以開(kāi)發(fā)視角為中心危虱,詳細(xì)列舉如何開(kāi)發(fā)更加高效羊娃、更加容錯(cuò)、更加有協(xié)作性埃跷,力求知其然蕊玷,更知其不然,結(jié)合正反例捌蚊,讓Java開(kāi)發(fā)者能夠提升協(xié)作效率集畅、提高代碼質(zhì)量。

image

碼出高效缅糟,碼出質(zhì)量挺智!

  • 你是否曾因Java代碼規(guī)范版本紛雜而無(wú)所適從?
  • 你是否想過(guò)代碼規(guī)范能將系統(tǒng)故障率降低20%窗宦?
  • 你是否曾因團(tuán)隊(duì)代碼風(fēng)格迥異而協(xié)同困難赦颇?
  • 你是否正在review一些原本可以避免的故障?
  • 你是否無(wú)法確定自己的代碼足夠健壯赴涵?
  • 阿里開(kāi)發(fā)手冊(cè)(黃山版)

一媒怯、編程規(guī)約

(一) 命名風(fēng)格

1.【強(qiáng)制】所有編程相關(guān)的命名均不能以下劃線或美元符號(hào)開(kāi)始,也不能以下劃線或美元符號(hào)結(jié)束髓窜。

  • 反例:_name / __name / Object / name\_ / name / Object$

2.【強(qiáng)制】所有編程相關(guān)的命名嚴(yán)禁使用拼音與英文混合的方式扇苞,更不允許直接使用中文的方式。

  • 說(shuō)明:正確的英文拼寫(xiě)和語(yǔ)法可以讓閱讀者易于理解寄纵,避免歧義鳖敷。注意,即使純拼音命名方式也要避免采用程拭。
  • 正例:ali / alibaba / taobao / kaikeba / aliyun / youku / hangzhou 等國(guó)際通用的名稱(chēng)定踱,可視同英文。
  • 反例:DaZhePromotion【打折】/ getPingfenByName()【評(píng)分】 / String fw【福娃】/ int 變量名 = 3

3.【強(qiáng)制】代碼和注釋中都要避免使用任何人類(lèi)語(yǔ)言中的種族歧視性或侮辱性詞語(yǔ)恃鞋。

  • 正例:blockList / allowList / secondary
  • 反例:blackList / whiteList / slave / SB / WTF

4.【強(qiáng)制】類(lèi)名使用 UpperCamelCase 風(fēng)格崖媚,以下情形例外:DO / PO / DTO / BO / VO / UID 等。

  • 正例:ForceCode / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion
  • 反例:forcecode / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion

5.【強(qiáng)制】方法名恤浪、參數(shù)名畅哑、成員變量、局部變量都統(tǒng)一使用 lowerCamelCase 風(fēng)格水由。

  • 正例:localValue / getHttpMessage() / inputUserId

6.【強(qiáng)制】常量命名應(yīng)該全部大寫(xiě)敢课,單詞間用下劃線隔開(kāi),力求語(yǔ)義表達(dá)完整清楚绷杜,不要嫌名字長(zhǎng)直秆。

  • 正例:MAX_STOCK_COUNT / CACHE_EXPIRED_TIME
  • 反例:MAX_COUNT / EXPIRED_TIME

7.【強(qiáng)制】抽象類(lèi)命名使用 Abstract 或 Base 開(kāi)頭;異常類(lèi)命名使用 Exception 結(jié)尾鞭盟,測(cè)試類(lèi)命名以它要

  • 測(cè)試的類(lèi)的名稱(chēng)開(kāi)始圾结,以 Test 結(jié)尾。

8.【強(qiáng)制】類(lèi)型與中括號(hào)緊挨相連來(lái)定義數(shù)組齿诉。

  • 正例:定義整形數(shù)組 int[] arrayDemo筝野。
  • 反例:在 main 參數(shù)中,使用 String args[] 來(lái)定義粤剧。

9.【強(qiáng)制】POJO 類(lèi)中的任何布爾類(lèi)型的變量歇竟,都不要加 is 前綴,否則部分框架解析會(huì)引起序列化錯(cuò)誤抵恋。

  • 說(shuō)明:本文 MySQL 規(guī)約中的建表約定第 1 條焕议,表達(dá)是與否的變量采用 is_xxx 的命名方式,所以需要在<resultMap>設(shè)置從 is_xxx 到 xxx 的映射關(guān)系弧关。
  • 反例:定義為基本數(shù)據(jù)類(lèi)型 Boolean isDeleted 的屬性盅安,它的方法也是 isDeleted(),框架在反向解析時(shí)世囊,“誤以為”對(duì)應(yīng)的屬性名稱(chēng)是 deleted别瞭,導(dǎo)致屬性獲取不到,進(jìn)而拋出異常株憾。

10.【強(qiáng)制】包名統(tǒng)一使用小寫(xiě)蝙寨,點(diǎn)分隔符之間有且僅有一個(gè)自然語(yǔ)義的英語(yǔ)單詞。包名統(tǒng)一使用單數(shù)形式嗤瞎,但是類(lèi)名如果有復(fù)數(shù)含義墙歪,類(lèi)名可以使用復(fù)數(shù)形式。

  • 正例:應(yīng)用工具類(lèi)包名為 com.alibaba.ei.kunlun.aap.util猫胁;類(lèi)名為 MessageUtils(此規(guī)則參考 spring 的框架結(jié)構(gòu))箱亿。

11.【強(qiáng)制】避免在子父類(lèi)的成員變量之間、或者不同代碼塊的局部變量之間采用完全相同的命名弃秆,使可理解性降低届惋。

  • 說(shuō)明:子類(lèi)、父類(lèi)成員變量名相同菠赚,即使是 public 也是能夠通過(guò)編譯脑豹,而局部變量在同一方法內(nèi)的不同代碼塊中同名也是合法的,但是要避免使用衡查。對(duì)于非 setter / getter 的參數(shù)名稱(chēng)也要避免與成員變量名稱(chēng)相同瘩欺。
  • 反例:
public class ConfusingName {
protected int stock;
protected String alibaba;
// 非 setter/getter 的參數(shù)名稱(chēng),不允許與本類(lèi)成員變量同名
public void access(String alibaba) {
if (condition) {
final int money = 666;
// ...
}
for (int i = 0; i < 10; i++) {
// 在同一方法體中,不允許與其它代碼塊中的 money 命名相同
final int money = 15978;
// ...
}
}
}
class Son extends ConfusingName {
// 不允許與父類(lèi)的成員變量名稱(chēng)相同
private int stock;
}

12.【強(qiáng)制】杜絕完全不規(guī)范的英文縮寫(xiě)俱饿,避免望文不知義歌粥。

  • 反例:AbstractClass“縮寫(xiě)”成 AbsClass;condition“縮寫(xiě)”成 condi拍埠;Function“縮寫(xiě)”成 Fu失驶,此類(lèi)隨意縮寫(xiě)嚴(yán)重降低了代碼的可閱讀性。

13.【推薦】為了達(dá)到代碼自解釋的目標(biāo)枣购,任何自定義編程元素在命名時(shí)嬉探,使用完整的單詞組合來(lái)表達(dá)。

  • 正例:在 JDK 中棉圈,對(duì)某個(gè)對(duì)象引用的 volatile 字段進(jìn)行原子更新的類(lèi)名為 AtomicReferenceFieldUpdater涩堤。
  • 反例:常見(jiàn)的方法內(nèi)變量為 int a; 的定義方式。

14.【推薦】在常量與變量命名時(shí)分瘾,表示類(lèi)型的名詞放在詞尾胎围,以提升辨識(shí)度。

  • 正例:startTime / workQueue / nameList / TERMINATED_THREAD_COUNT
  • 反例:startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD

15.【推薦】如果模塊芹敌、接口痊远、類(lèi)、方法使用了設(shè)計(jì)模式氏捞,在命名時(shí)要體現(xiàn)出具體模式碧聪。

  • 說(shuō)明:將設(shè)計(jì)模式體現(xiàn)在名字中,有利于閱讀者快速理解架構(gòu)設(shè)計(jì)思想液茎。
  • 正例:
public class OrderFactory;
public class LoginProxy;
public class ResourceObserver;

16.【推薦】接口類(lèi)中的方法和屬性不要加任何修飾符號(hào)(public 也不要加)逞姿,保持代碼的簡(jiǎn)潔性,并加上有效的 Javadoc 注釋捆等。盡量不要在接口里定義常量滞造,如果一定要定義,最好確定該常量與接口的方法相關(guān)栋烤,并且是整個(gè)應(yīng)用的基礎(chǔ)常量谒养。

  • 正例:接口方法簽名 void commit();接口基礎(chǔ)常量 String COMPANY = "alibaba";
  • 反例:接口方法定義 public abstract void commit();
  • 說(shuō)明:JDK8 中接口允許有默認(rèn)實(shí)現(xiàn),那么這個(gè) default 方法明郭,是對(duì)所有實(shí)現(xiàn)類(lèi)都有價(jià)值的默認(rèn)實(shí)現(xiàn)买窟。

17.接口和實(shí)現(xiàn)類(lèi)的命名有兩套規(guī)則:

1)【強(qiáng)制】對(duì)于 Service 和 DAO 類(lèi),基于 SOA 的理念薯定,暴露出來(lái)的服務(wù)一定是接口始绍,內(nèi)部的實(shí)現(xiàn)類(lèi)用 Impl 的后綴與接口區(qū)別。

  • 正例:CacheServiceImpl 實(shí)現(xiàn) CacheService 接口话侄。

2)【推薦】如果是形容能力的接口名稱(chēng)亏推,取對(duì)應(yīng)的形容詞為接口名(通常是 –able 結(jié)尾的形容詞)学赛。

  • 正例:AbstractTranslator 實(shí)現(xiàn) Translatable。

18.【參考】枚舉類(lèi)名帶上 Enum 后綴吞杭,枚舉成員名稱(chēng)需要全大寫(xiě)盏浇,單詞間用下劃線隔開(kāi)。

  • 說(shuō)明:枚舉其實(shí)就是特殊的常量類(lèi)篇亭,且構(gòu)造方法被默認(rèn)強(qiáng)制是私有缠捌。
  • 正例:枚舉名字為 ProcessStatusEnum 的成員名稱(chēng):SUCCESS / UNKNOWN_REASON

19.【參考】各層命名規(guī)約:

A)Service / DAO 層方法命名規(guī)約:

  • 1)獲取單個(gè)對(duì)象的方法用 get 做前綴。
  • 2)獲取多個(gè)對(duì)象的方法用 list 做前綴译蒂,復(fù)數(shù)結(jié)尾,如:listObjects
  • 3)獲取統(tǒng)計(jì)值的方法用 count 做前綴谊却。
  • 4)插入的方法用 save / insert 做前綴柔昼。
  • 5)刪除的方法用 remove / delete 做前綴。
  • 6)修改的方法用 update 做前綴炎辨。

B)領(lǐng)域模型命名規(guī)約:

  • 1)數(shù)據(jù)對(duì)象:xxxDO捕透,xxx 即為數(shù)據(jù)表名。
  • 2)數(shù)據(jù)傳輸對(duì)象:xxxDTO碴萧,xxx 為業(yè)務(wù)領(lǐng)域相關(guān)的名稱(chēng)乙嘀。
  • 3)展示對(duì)象:xxxVO,xxx 一般為網(wǎng)頁(yè)名稱(chēng)破喻。
  • 4)POJO 是 DO / DTO / BO / VO 的統(tǒng)稱(chēng)虎谢,禁止命名成 xxxPOJO。
image

(二) 常量定義

1.【強(qiáng)制】不允許任何魔法值(即未經(jīng)預(yù)先定義的常量)直接出現(xiàn)在代碼中曹质。

反例:

// 開(kāi)發(fā)者 A 定義了緩存的 key婴噩。
String key = "Id#taobao_" + tradeId;
cache.put(key, value);
// 開(kāi)發(fā)者 B 使用緩存時(shí)直接復(fù)制少了下劃線,即 key 是"Id#taobao" + tradeId羽德,導(dǎo)致出現(xiàn)故障几莽。
String key = "Id#taobao" + tradeId;
cache.get(key);

2.【強(qiáng)制】long 或 Long 賦值時(shí),數(shù)值后使用大寫(xiě) L宅静,不能是小寫(xiě) l章蚣,小寫(xiě)容易跟數(shù)字混淆,造成誤解姨夹。

  • 說(shuō)明:public static final Long NUM = 2l; 寫(xiě)的是數(shù)字的 21纤垂,還是 Long 型的 2?

3.【強(qiáng)制】浮點(diǎn)數(shù)類(lèi)型的數(shù)值后綴統(tǒng)一為大寫(xiě)的 D 或 F匀伏。

正例:

public static final double HEIGHT = 175.5D;
 public static final float WEIGHT = 150.3F;

4.【推薦】不要使用一個(gè)常量類(lèi)維護(hù)所有常量洒忧,要按常量功能進(jìn)行歸類(lèi),分開(kāi)維護(hù)够颠。

  • 說(shuō)明:大而全的常量類(lèi)熙侍,雜亂無(wú)章,使用查找功能才能定位到要修改的常量,不利于理解蛉抓,也不利于維護(hù)庆尘。
  • 正例:緩存相關(guān)常量放在類(lèi) CacheConsts 下;系統(tǒng)配置相關(guān)常量放在類(lèi) SystemConfigConsts 下巷送。

5.【推薦】常量的復(fù)用層次有五層:跨應(yīng)用共享常量驶忌、應(yīng)用內(nèi)共享常量、子工程內(nèi)共享常量笑跛、包內(nèi)共享常量付魔、類(lèi)內(nèi)共享常量。

  • 1)跨應(yīng)用共享常量:放置在二方庫(kù)中飞蹂,通常是 client.jar 中的 constant 目錄下几苍。
  • 2)應(yīng)用內(nèi)共享常量:放置在一方庫(kù)中,通常是子模塊中的 constant 目錄下陈哑。

反例:易懂常量也要統(tǒng)一定義成應(yīng)用內(nèi)共享常量捅暴,兩個(gè)程序員在兩個(gè)類(lèi)中分別定義了表示“是”的常量:

類(lèi) A 中:public static final String YES = "yes";
類(lèi) B 中:public static final String YES = "y";

A.YES.equals(B.YES)秩彤,預(yù)期是 true透典,但實(shí)際返回為 false哀蘑,導(dǎo)致線上問(wèn)題。

  • 3)子工程內(nèi)部共享常量:即在當(dāng)前子工程的 constant 目錄下界酒。
  • 4)包內(nèi)共享常量:即在當(dāng)前包下單獨(dú)的 constant 目錄下圣拄。
  • 5)類(lèi)內(nèi)共享常量:直接在類(lèi)內(nèi)部 private static final 定義。

6.【推薦】如果變量值僅在一個(gè)固定范圍內(nèi)變化用 enum 類(lèi)型來(lái)定義盾计。

說(shuō)明:如果存在名稱(chēng)之外的延伸屬性應(yīng)使用 enum 類(lèi)型售担,下面正例中的數(shù)字就是延伸信息,表示一年中的第幾個(gè)季節(jié)署辉。

正例:

public enum SeasonEnum {
SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);
private int seq;
SeasonEnum(int seq) {
this.seq = seq;
}
public int getSeq() {
return seq;
}
}

(三) 代碼格式

9.【強(qiáng)制】方法參數(shù)在定義和傳入時(shí)族铆,多個(gè)參數(shù)逗號(hào)后面必須加空格。

  • 正例:下例中實(shí)參的 args1 逗號(hào)后邊必須要有一個(gè)空格哭尝。
method(args1, args2, args3);

10.【強(qiáng)制】IDE 的 text file encoding 設(shè)置為 UTF-8哥攘;IDE 中文件的換行符使用 Unix 格式,不要使用Windows 格式材鹦。

11.【推薦】單個(gè)方法的總行數(shù)不超過(guò) 80 行逝淹。

  • 說(shuō)明:除注釋之外的方法簽名、左右大括號(hào)桶唐、方法內(nèi)代碼栅葡、空行、回車(chē)及任何不可見(jiàn)字符的總行數(shù)不超過(guò) 80 行尤泽。
  • 正例:代碼邏輯分清紅花和綠葉欣簇,個(gè)性和共性规脸,綠葉邏輯單獨(dú)出來(lái)成為額外方法,使主干代碼更加晰熊咽;共性邏輯抽取成為共性方法莫鸭,便于復(fù)用和維護(hù)。

12.【推薦】沒(méi)有必要增加若干空格來(lái)使變量的賦值等號(hào)與上一行對(duì)應(yīng)位置的等號(hào)對(duì)齊横殴。

正例:

int one = 1;
long two = 2L;
float three = 3F;
StringBuilder builder = new StringBuilder();
  • 說(shuō)明:增加 builder 這個(gè)變量被因,如果需要對(duì)齊,則給 one衫仑、two梨与、three 都要增加幾個(gè)空格,在變量比較多的情況下惑畴,是非常累贅的事情蛋欣。

13.【推薦】不同邏輯、不同語(yǔ)義如贷、不同業(yè)務(wù)的代碼之間插入一個(gè)空行,分隔開(kāi)來(lái)以提升可讀性到踏。

  • 說(shuō)明:任何情形杠袱,沒(méi)有必要插入多個(gè)空行進(jìn)行隔開(kāi)。

(四) OOP規(guī)約

23.【推薦】循環(huán)體內(nèi)窝稿,字符串的連接方式楣富,使用 StringBuilder 的 append 方法進(jìn)行擴(kuò)展。

反例:

String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
  • 說(shuō)明:反編譯出的字節(jié)碼文件顯示每次循環(huán)都會(huì) new 出一個(gè) StringBuilder 對(duì)象伴榔,然后進(jìn)行 append 操作纹蝴,最后通過(guò)toString() 返回 String 對(duì)象,造成內(nèi)存資源浪費(fèi)踪少。

24.【推薦】final 可以聲明類(lèi)塘安、成員變量、方法援奢、以及本地變量兼犯,下列情況使用 final 關(guān)鍵字:

  • 1)不允許被繼承的類(lèi),如:String 類(lèi)集漾。
  • 2)不允許修改引用的域?qū)ο笄星纾篜OJO 類(lèi)的域變量。
  • 3)不允許被覆寫(xiě)的方法具篇,如:POJO 類(lèi)的 setter 方法纬霞。
  • 4)不允許運(yùn)行過(guò)程中重新賦值的局部變量。
  • 5)避免上下文重復(fù)使用一個(gè)變量驱显,使用 final 關(guān)鍵字可以強(qiáng)制重新定義一個(gè)變量诗芜,方便更好地進(jìn)行重構(gòu)瞳抓。

25.【推薦】慎用 Object 的 clone 方法來(lái)拷貝對(duì)象。

  • 說(shuō)明:對(duì)象 clone 方法默認(rèn)是淺拷貝绢陌,若想實(shí)現(xiàn)深拷貝需覆寫(xiě) clone 方法實(shí)現(xiàn)域?qū)ο蟮纳疃缺闅v式拷貝挨下。

26.【推薦】類(lèi)成員與方法訪問(wèn)控制從嚴(yán):

  • 1)如果不允許外部直接通過(guò) new 來(lái)創(chuàng)建對(duì)象,那么構(gòu)造方法必須是 private脐湾。
  • 2)工具類(lèi)不允許有 public 或 default 構(gòu)造方法臭笆。
  • 3)類(lèi)非 static 成員變量并且與子類(lèi)共享,必須是 protected秤掌。
  • 4)類(lèi)非 static 成員變量并且僅在本類(lèi)使用愁铺,必須是 private。
  • 5)類(lèi) static 成員變量如果僅在本類(lèi)使用闻鉴,必須是 private茵乱。
  • 6)若是 static 成員變量,考慮是否為 final孟岛。
  • 7)類(lèi)成員方法只供類(lèi)內(nèi)部調(diào)用瓶竭,必須是 private。
  • 8)類(lèi)成員方法只對(duì)繼承類(lèi)公開(kāi)渠羞,那么限制為 protected斤贰。

說(shuō)明:任何類(lèi)、方法次询、參數(shù)荧恍、變量,嚴(yán)控訪問(wèn)范圍屯吊。過(guò)于寬泛的訪問(wèn)范圍送巡,不利于模塊解耦。思考:如果是一個(gè)private 的方法盒卸,想刪除就刪除骗爆,可是一個(gè) public 的 service 成員方法或成員變量,刪除一下世落,不得手心冒點(diǎn)汗嗎淮腾?變量像自己的小孩,盡量在自己的視線內(nèi)屉佳,變量作用域太大谷朝,無(wú)限制的到處跑,那么你會(huì)擔(dān)心的武花。

(五) 日期時(shí)間

1.【強(qiáng)制】日期格式化時(shí)圆凰,傳入 pattern 中表示年份統(tǒng)一使用小寫(xiě)的 y。

  • 說(shuō)明:日期格式化時(shí)体箕,yyyy 表示當(dāng)天所在的年专钉,而大寫(xiě)的 YYYY 代表是 week in which year(JDK7 之后引入的概念)挑童,意思是當(dāng)天所在的周屬于的年份,一周從周日開(kāi)始跃须,周六結(jié)束站叼,只要本周跨年,返回的 YYYY 就是下一年菇民。
  • 正例:表示日期和時(shí)間的格式如下所示:
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
  • 反例:某程序員因使用 YYYY/MM/dd 進(jìn)行日期格式化尽楔,2017/12/31 執(zhí)行結(jié)果為 2018/12/31,造成線上故障第练。

2.【強(qiáng)制】在日期格式中分清楚大寫(xiě)的 M 和小寫(xiě)的 m阔馋,大寫(xiě)的 H 和小寫(xiě)的 h 分別指代的意義。

  • 說(shuō)明:日期格式中的這兩對(duì)字母表意如下:
  • 1)表示月份是大寫(xiě)的 M
  • 2)表示分鐘則是小寫(xiě)的 m
  • 3)24 小時(shí)制的是大寫(xiě)的 H
  • 4)12 小時(shí)制的則是小寫(xiě)的 h

3.【強(qiáng)制】獲取當(dāng)前毫秒數(shù):System.currentTimeMillis()娇掏;而不是 new Date().getTime()呕寝。

  • 說(shuō)明:獲取納秒級(jí)時(shí)間,則使用 System.nanoTime 的方式婴梧。在 JDK8 中下梢,針對(duì)統(tǒng)計(jì)時(shí)間等場(chǎng)景,推薦使用 Instant 類(lèi)塞蹭。

4.【強(qiáng)制】不允許在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp怔球。

  • 說(shuō)明:第 1 個(gè)不記錄時(shí)間,getHours() 拋出異常浮还;第 2 個(gè)不記錄日期,getYear() 拋出異常闽巩;第 3 個(gè)在構(gòu)造方法
super((time / 1000) * 1000)钧舌,在 Timestamp 屬性 fastTime 和 nanos 分別存儲(chǔ)秒和納秒信息。
  • 反例:java.util.Date.after(Date) 進(jìn)行時(shí)間比較時(shí)涎跨,當(dāng)入?yún)⑹?java.sql.Timestamp 時(shí)洼冻,會(huì)觸發(fā) JDK BUG(JDK9 已修復(fù)),可能導(dǎo)致比較時(shí)的意外結(jié)果隅很。

5.【強(qiáng)制】禁止在程序中寫(xiě)死一年為 365 天撞牢,避免在公歷閏年時(shí)出現(xiàn)日期轉(zhuǎn)換錯(cuò)誤或程序邏輯錯(cuò)誤。

正例:

// 獲取今年的天數(shù)
int daysOfThisYear = LocalDate.now().lengthOfYear();
// 獲取指定某年的天數(shù)
LocalDate.of(2011, 1, 1).lengthOfYear();

反例:

// 第一種情況:在閏年 366 天時(shí)叔营,出現(xiàn)數(shù)組越界異常
int[] dayArray = new int[365];
// 第二種情況:一年有效期的會(huì)員制屋彪,2020 年 1 月 26 日注冊(cè),硬編碼 365 返回的卻是 2021 年 1 月 25 日
Calendar calendar = Calendar.getInstance();
calendar.set(2020, 1, 26);
calendar.add(Calendar.DATE, 365);

6.【推薦】避免公歷閏年 2 月問(wèn)題绒尊。閏年的 2 月份有 29 天畜挥,一年后的那一天不可能是 2 月 29 日。

7.【推薦】使用枚舉值來(lái)指代月份婴谱。如果使用數(shù)字蟹但,注意 Date躯泰,Calendar 等日期相關(guān)類(lèi)的月份 month 取值范圍從 0 到 11 之間。

  • 說(shuō)明:參考 JDK 原生注釋?zhuān)琈onth value is 0-based. e.g., 0 for January.
  • 正例:Calendar.JANUARY华糖,Calendar.FEBRUARY麦向,Calendar.MARCH 等來(lái)指代相應(yīng)月份來(lái)進(jìn)行傳參或比較

(六) 集合處理

15.【強(qiáng)制】在 JDK7 版本及以上,Comparator 實(shí)現(xiàn)類(lèi)要滿足如下三個(gè)條件客叉,不然 Arrays.sort诵竭,Collections.sort 會(huì)拋 IllegalArgumentException 異常。

說(shuō)明:三個(gè)條件如下

  • 1)x十办,y 的比較結(jié)果和 y秀撇,x 的比較結(jié)果相反。
  • 2)x > y向族,y > z呵燕,則 x > z。
  • 3)x = y件相,則 x再扭,z 比較結(jié)果和 y,z 比較結(jié)果相同夜矗。

反例:下例中沒(méi)有處理相等的情況泛范,交換兩個(gè)對(duì)象判斷結(jié)果并不互反,不符合第一個(gè)條件紊撕,在實(shí)際使用中可能會(huì)出現(xiàn)異常罢荡。

new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() > o2.getId() ? 1 : -1;
}
};

16.【推薦】泛型集合使用時(shí),在 JDK7 及以上对扶,使用 diamond 語(yǔ)法或全省略区赵。

說(shuō)明:菱形泛型,即 diamond浪南,直接使用<>來(lái)指代前邊已經(jīng)指定的類(lèi)型笼才。

正例:

// diamond 方式,即<>
HashMap<String, String> userCache = new HashMap<>(16);
// 全省略方式
ArrayList<User> users = new ArrayList(10);

17.【推薦】集合初始化時(shí)络凿,指定集合初始值大小骡送。

  • 說(shuō)明:HashMap 使用構(gòu)造方法 HashMap(int initialCapacity) 進(jìn)行初始化時(shí),如果暫時(shí)無(wú)法確定集合大小絮记,那么指定默認(rèn)值(16)即可摔踱。
  • 正例:initialCapacity = (需要存儲(chǔ)的元素個(gè)數(shù) / 負(fù)載因子) + 1。注意負(fù)載因子(即 loaderfactor)默認(rèn)為 0.75到千,如果暫時(shí)無(wú)法確定初始值大小昌渤,請(qǐng)?jiān)O(shè)置為 16(即默認(rèn)值)。
  • 反例:HashMap 需要放置 1024 個(gè)元素憔四,由于沒(méi)有設(shè)置容量初始大小膀息,隨著元素增加而被迫不斷擴(kuò)容般眉,resize() 方法總共會(huì)調(diào)用 8 次,反復(fù)重建哈希表和數(shù)據(jù)遷移潜支。當(dāng)放置的集合元素個(gè)數(shù)達(dá)千萬(wàn)級(jí)時(shí)會(huì)影響程序性能甸赃。

18.【推薦】使用 entrySet 遍歷 Map 類(lèi)集合 KV,而不是 keySet 方式進(jìn)行遍歷冗酿。

  • 說(shuō)明:keySet 其實(shí)是遍歷了 2 次埠对,一次是轉(zhuǎn)為 Iterator 對(duì)象,另一次是從 hashMap 中取出 key 所對(duì)應(yīng)的 value裁替。而只是遍歷了一次就把 key 和 value 都放到了 entry 中项玛,效率更高。如果是 JDK8弱判,使用 Map.forEach 方法襟沮。
  • 正例:values() 返回的是 V 值集合,是一個(gè) list 集合對(duì)象昌腰;keySet() 返回的是 K 值集合开伏,是一個(gè) Set 集合對(duì)象;entrySet() 返回的是 K-V 值組合的 Set 集合遭商。

(七) 并發(fā)處理

13.【推薦】資金相關(guān)的金融敏感信息固灵,使用悲觀鎖策略。

  • 說(shuō)明:樂(lè)觀鎖在獲得鎖的同時(shí)已經(jīng)完成了更新操作劫流,校驗(yàn)邏輯容易出現(xiàn)漏洞巫玻,另外,樂(lè)觀鎖對(duì)沖突的解決策略有較復(fù)雜的要求祠汇,處理不當(dāng)容易造成系統(tǒng)壓力或數(shù)據(jù)異常大审,所以資金相關(guān)的金融敏感信息不建議使用樂(lè)觀鎖更新。
  • 正例:悲觀鎖遵循一鎖二判三更新四釋放的原則座哩。

14.【推薦】使用 CountDownLatch 進(jìn)行異步轉(zhuǎn)同步操作,每個(gè)線程退出前必須調(diào)用 countDown 方法粮彤,線程執(zhí)行代碼注意 catch 異常根穷,確保 countDown 方法被執(zhí)行到,避免主線程無(wú)法執(zhí)行至 await 方法导坟,直到超時(shí)才返回結(jié)果屿良。

  • 說(shuō)明:注意,子線程拋出異常堆棧惫周,不能在主線程 try-catch 到尘惧。

15.【推薦】避免 Random 實(shí)例被多線程使用,雖然共享該實(shí)例是線程安全的递递,但會(huì)因競(jìng)爭(zhēng)同一 seed 導(dǎo)致的性能下降喷橙。

  • 說(shuō)明:Random 實(shí)例包括 java.util.Random 的實(shí)例或者 Math.random() 的方式啥么。
  • 正例:在 JDK7 之后,可以直接使用 API ThreadLocalRandom贰逾,而在 JDK7 之前悬荣,需要編碼保證每個(gè)線程持有一個(gè)單獨(dú)的 Random 實(shí)例。

16.【推薦】通過(guò)雙重檢查鎖(double-checked locking)疙剑,實(shí)現(xiàn)延遲初始化需要將目標(biāo)屬性聲明為volatile 型氯迂,(比如修改 helper 的屬性聲明為 private volatile Helper helper = null;)。

正例:

public class LazyInitDemo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
// other methods and fields...
}

17.【參考】volatile 解決多線程內(nèi)存不可見(jiàn)問(wèn)題對(duì)于一寫(xiě)多讀言缤,是可以解決變量同步問(wèn)題嚼蚀,但是如果多寫(xiě),同樣無(wú)法解決線程安全問(wèn)題管挟。

說(shuō)明:如果是 count++操作轿曙,使用如下類(lèi)實(shí)現(xiàn):

AtomicInteger count = new AtomicInteger();
count.addAndGet(1);

如果是 JDK8,推薦使用 LongAdder 對(duì)象哮独,比 AtomicLong 性能更好(減少樂(lè)觀鎖的重試次數(shù))拳芙。

18.【參考】HashMap 在容量不夠進(jìn)行 resize 時(shí)由于高并發(fā)可能出現(xiàn)死鏈,導(dǎo)致 CPU 飆升皮璧,在開(kāi)發(fā)過(guò)程中注意規(guī)避此風(fēng)險(xiǎn)舟扎。

19.【參考】ThreadLocal 對(duì)象使用 static 修飾,ThreadLocal 無(wú)法解決共享對(duì)象的更新問(wèn)題悴务。

  • 說(shuō)明:這個(gè)變量是針對(duì)一個(gè)線程內(nèi)所有操作共享的褐澎,所以設(shè)置為靜態(tài)變量错览,所有此類(lèi)實(shí)例共享此靜態(tài)變量,也就是說(shuō)在
  • 類(lèi)第一次被使用時(shí)裝載,只分配一塊存儲(chǔ)空間慕爬,所有此類(lèi)的對(duì)象(只要是這個(gè)線程內(nèi)定義的)都可以操控這個(gè)變量。

(八) 控制語(yǔ)句

10.【推薦】循環(huán)體中的語(yǔ)句要考量性能飘弧,以下操作盡量移至循環(huán)體外處理携栋,如定義對(duì)象、變量挖垛、獲取數(shù)據(jù)庫(kù)連接痒钝,進(jìn)行不必要的 try-catch 操作(這個(gè) try-catch 是否可以移至循環(huán)體外)。

11.【推薦】避免采用取反邏輯運(yùn)算符痢毒。

  • 說(shuō)明:取反邏輯不利于快速理解送矩,并且取反邏輯寫(xiě)法一般都存在對(duì)應(yīng)的正向邏輯寫(xiě)法。
  • 正例:使用 if(x < 628) 來(lái)表達(dá) x 小于 628哪替。
  • 反例:使用 if(!(x >= 628)) 來(lái)表達(dá) x 小于 628栋荸。

12.【推薦】公開(kāi)接口需要進(jìn)行入?yún)⒈Wo(hù),尤其是批量操作的接口。

  • 反例:某業(yè)務(wù)系統(tǒng)晌块,提供一個(gè)用戶(hù)批量查詢(xún)的接口爱沟,API 文檔上有說(shuō)最多查多少個(gè),但接口實(shí)現(xiàn)上沒(méi)做任何保護(hù)摸袁,導(dǎo)致調(diào)用方傳了一個(gè) 1000 的用戶(hù) id 數(shù)組過(guò)來(lái)后钥顽,查詢(xún)信息后,內(nèi)存爆了靠汁。

13.【參考】下列情形蜂大,需要進(jìn)行參數(shù)校驗(yàn):

  • 1)調(diào)用頻次低的方法。
  • 2)執(zhí)行時(shí)間開(kāi)銷(xiāo)很大的方法蝶怔。此情形中奶浦,參數(shù)校驗(yàn)時(shí)間幾乎可以忽略不計(jì),但如果因?yàn)閰?shù)錯(cuò)誤導(dǎo)致中間執(zhí)行回
  • 退踢星,或者錯(cuò)誤澳叉,那得不償失。
  • 3)需要極高穩(wěn)定性和可用性的方法沐悦。
  • 4)對(duì)外提供的開(kāi)放接口成洗,不管是 RPC / API / HTTP 接口。
  • 5)敏感權(quán)限入口藏否。

14.【參考】下列情形瓶殃,不需要進(jìn)行參數(shù)校驗(yàn):

  • 1)極有可能被循環(huán)調(diào)用的方法。但在方法說(shuō)明里必須注明外部參數(shù)檢查副签。
  • 2)底層調(diào)用頻度比較高的方法遥椿。畢竟是像純凈水過(guò)濾的最后一道,參數(shù)錯(cuò)誤不太可能到底層才會(huì)暴露問(wèn)題淆储。一般 DAO層與 Service 層都在同一個(gè)應(yīng)用中冠场,部署在同一臺(tái)服務(wù)器中,所以 DAO 的參數(shù)校驗(yàn)本砰,可以省略碴裙。
  • 3)被聲明成 private 只會(huì)被自己代碼所調(diào)用的方法,如果能夠確定調(diào)用方法的代碼傳入?yún)?shù)已經(jīng)做過(guò)檢查或者肯定不會(huì)有問(wèn)題点额,此時(shí)可以不校驗(yàn)參數(shù)青团。

(九) 注釋規(guī)約

10.【參考】對(duì)于注釋的要求:第一、能夠準(zhǔn)確反映設(shè)計(jì)思想和代碼邏輯咖楣;第二、能夠描述業(yè)務(wù)含義芦昔,使別的程序員能夠迅速了解到代碼背后的信息诱贿。完全沒(méi)有注釋的大段代碼對(duì)于閱讀者形同天書(shū),注釋是給自己看的,即使隔很長(zhǎng)時(shí)間珠十,也能清晰理解當(dāng)時(shí)的思路料扰;注釋也是給繼任者看的,使其能夠快速接替自己的工作焙蹭。

11.【參考】好的命名晒杈、代碼結(jié)構(gòu)是自解釋的,注釋力求精簡(jiǎn)準(zhǔn)確孔厉、表達(dá)到位拯钻。避免出現(xiàn)注釋的另一個(gè)極端:過(guò)多過(guò)濫的注釋?zhuān)a的邏輯一旦修改,修改注釋又是相當(dāng)大的負(fù)擔(dān)撰豺。

反例:

// put elephant into fridge
put(elephant, fridge);

方法名 put粪般,加上兩個(gè)有意義的變量名稱(chēng) elephant 和 fridge,已經(jīng)說(shuō)明了這是在干什么污桦,語(yǔ)義清晰的代碼不需要額外的注釋亩歹。

12.【參考】特殊注釋標(biāo)記,請(qǐng)注明標(biāo)記人與標(biāo)記時(shí)間凡橱。注意及時(shí)處理這些標(biāo)記小作,通過(guò)標(biāo)記掃描,經(jīng)常清理此類(lèi)標(biāo)記稼钩。線上故障有時(shí)候就是來(lái)源于這些標(biāo)記處的代碼顾稀。

  • 1)待辦事宜(TODO):(標(biāo)記人,標(biāo)記時(shí)間变抽,[預(yù)計(jì)處理時(shí)間])表示需要實(shí)現(xiàn)础拨,但目前還未實(shí)現(xiàn)的功能。這實(shí)際上是一個(gè) Javadoc 的標(biāo)簽绍载,目前的 Javadoc 還沒(méi)有實(shí)現(xiàn)诡宗,但已經(jīng)被廣泛使用。只能應(yīng)用于類(lèi)击儡,接口和方法(因?yàn)樗且粋€(gè)Javadoc 標(biāo)簽)塔沃。
  • 2)錯(cuò)誤,不能工作(FIXME):(標(biāo)記人阳谍,標(biāo)記時(shí)間蛀柴,[預(yù)計(jì)處理時(shí)間])在注釋中用 FIXME 標(biāo)記某代碼是錯(cuò)誤的,而且不能工作矫夯,需要及時(shí)糾正的情況鸽疾。

二、異常日志

(一) 錯(cuò)誤碼

10.【參考】錯(cuò)誤碼分為一級(jí)宏觀錯(cuò)誤碼训貌、二級(jí)宏觀錯(cuò)誤碼制肮、三級(jí)宏觀錯(cuò)誤碼冒窍。

  • 說(shuō)明:在無(wú)法更加具體確定的錯(cuò)誤場(chǎng)景中,可以直接使用一級(jí)宏觀錯(cuò)誤碼豺鼻,分別是:A0001(用戶(hù)端錯(cuò)誤)综液、B0001(系統(tǒng)執(zhí)行出錯(cuò))、C0001(調(diào)用第三方服務(wù)出錯(cuò))儒飒。
  • 正例:調(diào)用第三方服務(wù)出錯(cuò)是一級(jí)谬莹,中間件錯(cuò)誤是二級(jí),消息服務(wù)出錯(cuò)是三級(jí)桩了。

11.【參考】錯(cuò)誤碼的后三位編號(hào)與 HTTP 狀態(tài)碼沒(méi)有任何關(guān)系附帽。

12.【參考】錯(cuò)誤碼有利于不同文化背景的開(kāi)發(fā)者進(jìn)行交流與代碼協(xié)作。

說(shuō)明:英文單詞形式的錯(cuò)誤碼不利于非英語(yǔ)母語(yǔ)國(guó)家(如阿拉伯語(yǔ)圣猎、希伯來(lái)語(yǔ)士葫、俄羅斯語(yǔ)等)之間的開(kāi)發(fā)者互相協(xié)作。

13.【參考】錯(cuò)誤碼即人性送悔,感性認(rèn)知+口口相傳慢显,使用純數(shù)字來(lái)進(jìn)行錯(cuò)誤碼編排不利于感性記憶和分類(lèi)。

  • 說(shuō)明:數(shù)字是一個(gè)整體欠啤,每位數(shù)字的地位和含義是相同的荚藻。
  • 反例:一個(gè)五位數(shù)字 12345,第 1 位是錯(cuò)誤等級(jí)洁段,第 2 位是錯(cuò)誤來(lái)源应狱,345 是編號(hào),人的大腦不會(huì)主動(dòng)地拆開(kāi)并分辨每位數(shù)字的不同含義祠丝。

(二) 異常處理

11.【推薦】防止 NPE疾呻,是程序員的基本修養(yǎng),注意 NPE 產(chǎn)生的場(chǎng)景:

  • 1)返回類(lèi)型為基本數(shù)據(jù)類(lèi)型写半,return 包裝數(shù)據(jù)類(lèi)型的對(duì)象時(shí)岸蜗,自動(dòng)拆箱有可能產(chǎn)生 NPE
  • 反例:public int method() { return Integer 對(duì)象; },如果為 null叠蝇,自動(dòng)解箱拋 NPE璃岳。
  • 2)數(shù)據(jù)庫(kù)的查詢(xún)結(jié)果可能為 null。
  • 3)集合里的元素即使 isNotEmpty悔捶,取出的數(shù)據(jù)元素也可能為 null铃慷。
  • 4)遠(yuǎn)程調(diào)用返回對(duì)象時(shí),一律要求進(jìn)行空指針判斷蜕该,防止 NPE犁柜。
  • 5)對(duì)于 Session 中獲取的數(shù)據(jù),建議進(jìn)行 NPE 檢查堂淡,避免空指針馋缅。
  • 6)級(jí)聯(lián)調(diào)用 obj.getA().getB().getC()坛怪;一連串調(diào)用,易產(chǎn)生 NPE股囊。

正例:使用 JDK8 的 Optional 類(lèi)來(lái)防止 NPE 問(wèn)題。

12.【推薦】定義時(shí)區(qū)分 unchecked / checked 異常更啄,避免直接拋出 new RuntimeException()稚疹,更不允許拋出 Exception 或者 Throwable,應(yīng)使用有業(yè)務(wù)含義的自定義異常祭务。推薦業(yè)界已定義過(guò)的自定義異常内狗,如:DAOException / ServiceException 等。

13.【參考】對(duì)于公司外的 http / api 開(kāi)放接口必須使用錯(cuò)誤碼义锥,而應(yīng)用內(nèi)部推薦異常拋出柳沙;跨應(yīng)用間RPC 調(diào)用優(yōu)先考慮使用 Result 方式,封裝 isSuccess() 方法拌倍、錯(cuò)誤碼赂鲤、錯(cuò)誤簡(jiǎn)短信息;應(yīng)用內(nèi)部推薦異常拋出柱恤。

說(shuō)明:關(guān)于 RPC 方法返回方式使用 Result 方式的理由:

1)使用拋異常返回方式,調(diào)用方如果沒(méi)有捕獲到就會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤。

2)如果不加棧信息清蚀,只是 new 自定義異常赚瘦,加入自己的理解的 error message,對(duì)于調(diào)用端解決問(wèn)題的幫助不會(huì)太多寺谤。

如果加了棧信息仑鸥,在頻繁調(diào)用出錯(cuò)的情況下,數(shù)據(jù)序列化和傳輸?shù)男阅軗p耗也是問(wèn)題变屁。

三眼俊、單元測(cè)試

12.【推薦】對(duì)于不可測(cè)的代碼在適當(dāng)?shù)臅r(shí)機(jī)做必要的重構(gòu),使代碼變得可測(cè)避免為了達(dá)到測(cè)試要求而書(shū)寫(xiě)不規(guī)范測(cè)試代碼敞贡。

13.【推薦】在設(shè)計(jì)評(píng)審階段泵琳,開(kāi)發(fā)人員需要和測(cè)試人員一起確定單元測(cè)試范圍,單元測(cè)試最好覆蓋所有測(cè)試用例(UC)誊役。

14.【推薦】單元測(cè)試作為一種質(zhì)量保障手段获列,在項(xiàng)目提測(cè)前完成單元測(cè)試,不建議項(xiàng)目發(fā)布后補(bǔ)充單元測(cè)試用例蛔垢。

15.【參考】為了更方便地進(jìn)行單元測(cè)試击孩,業(yè)務(wù)代碼應(yīng)避免以下情況:

  • 構(gòu)造方法中做的事情過(guò)多。
  • 存在過(guò)多的全局變量和靜態(tài)方法鹏漆。
  • 存在過(guò)多的外部依賴(lài)巩梢。
  • 存在過(guò)多的條件語(yǔ)句创泄。

說(shuō)明:多層條件語(yǔ)句建議使用衛(wèi)語(yǔ)句、策略模式括蝠、狀態(tài)模式等方式重構(gòu)鞠抑。

16.【參考】不要對(duì)單元測(cè)試存在如下誤解:

  • 那是測(cè)試同學(xué)干的事情。本文是開(kāi)發(fā)手冊(cè)忌警,凡是本文內(nèi)容都是與開(kāi)發(fā)同學(xué)強(qiáng)相關(guān)的搁拙。
  • 單元測(cè)試代碼是多余的。系統(tǒng)的整體功能與各單元部件的測(cè)試正常與否是強(qiáng)相關(guān)的法绵。
  • 單元測(cè)試代碼不需要維護(hù)箕速。一年半載后,那么單元測(cè)試幾乎處于廢棄狀態(tài)朋譬。
  • 單元測(cè)試與線上故障沒(méi)有辯證關(guān)系盐茎。好的單元測(cè)試能夠最大限度地規(guī)避線上故障。

四徙赢、安全規(guī)約

8.【強(qiáng)制】在使用平臺(tái)資源字柠,譬如短信、郵件犀忱、電話募谎、下單、支付阴汇,必須實(shí)現(xiàn)正確的防重放的機(jī)制数冬,如數(shù)量限制、疲勞度控制搀庶、驗(yàn)證碼校驗(yàn)拐纱,避免被濫刷而導(dǎo)致資損。

說(shuō)明:如注冊(cè)時(shí)發(fā)送驗(yàn)證碼到手機(jī)哥倔,如果沒(méi)有限制次數(shù)和頻率秸架,那么可以利用此功能騷擾到其它用戶(hù),并造成短信平臺(tái)資源浪費(fèi)咆蒿。

9.【強(qiáng)制】對(duì)于文件上傳功能东抹,需要對(duì)于文件大小、類(lèi)型進(jìn)行嚴(yán)格檢查和控制沃测。

說(shuō)明:攻擊者可以利用上傳漏洞缭黔,上傳惡意文件到服務(wù)器,并且遠(yuǎn)程執(zhí)行蒂破,達(dá)到控制網(wǎng)站服務(wù)器的目的馏谨。

10.【強(qiáng)制】配置文件中的密碼需要加密。

11.【推薦】發(fā)貼附迷、評(píng)論惧互、發(fā)送等即時(shí)消息哎媚,需要用戶(hù)輸入內(nèi)容的場(chǎng)景。必須實(shí)現(xiàn)防刷喊儡、內(nèi)容違禁詞過(guò)濾等風(fēng)控策略拨与。

五、MySQL數(shù)據(jù)庫(kù)

(一) 建表規(guī)約

15.【推薦】單表行數(shù)超過(guò) 500 萬(wàn)行或者單表容量超過(guò) 2GB艾猜,才推薦進(jìn)行分庫(kù)分表截珍。

說(shuō)明:如果預(yù)計(jì)三年后的數(shù)據(jù)量根本達(dá)不到這個(gè)級(jí)別,請(qǐng)不要在創(chuàng)建表時(shí)就分庫(kù)分表箩朴。

16.【參考】合適的字符存儲(chǔ)長(zhǎng)度,不但節(jié)約數(shù)據(jù)庫(kù)表空間秋度、節(jié)約索引存儲(chǔ)炸庞,更重要的是提升檢索速度。

正例:無(wú)符號(hào)值可以避免誤存負(fù)數(shù)荚斯,且擴(kuò)大了表示范圍:

(二) 索引規(guī)約

9.【推薦】建組合索引的時(shí)候埠居,區(qū)分度最高的在最左邊。

  • 正例:如果 where a = ? and b = ?事期,a 列的幾乎接近于唯一值滥壕,那么只需要單建 idx_a 索引即可。
  • 說(shuō)明:存在非等號(hào)和等號(hào)混合判斷條件時(shí)兽泣,在建索引時(shí)绎橘,請(qǐng)把等號(hào)條件的列前置。如:where c > ? and d = ? 那么即使c 的區(qū)分度更高唠倦,也必須把 d 放在索引的最前列称鳞,即建立組合索引 idx_d_c。

10.【推薦】防止因字段類(lèi)型不同造成的隱式轉(zhuǎn)換稠鼻,導(dǎo)致索引失效冈止。

11.【參考】創(chuàng)建索引時(shí)避免有如下極端誤解:

  • 1)索引寧濫勿缺。認(rèn)為一個(gè)查詢(xún)就需要建一個(gè)索引候齿。
  • 2)吝嗇索引的創(chuàng)建熙暴。認(rèn)為索引會(huì)消耗空間、嚴(yán)重拖慢記錄的更新以及行的新增速度慌盯。
  • 3)抵制唯一索引周霉。認(rèn)為唯一索引一律需要在應(yīng)用層通過(guò)“先查后插”方式解決。

(三) SQL語(yǔ)句

11.【推薦】in 操作能避免則避免润匙,若實(shí)在避免不了诗眨,需要仔細(xì)評(píng)估 in 后邊的集合元素?cái)?shù)量,控制在1000 個(gè)之內(nèi)孕讳。

12.【參考】因國(guó)際化需要匠楚,所有的字符存儲(chǔ)與表示巍膘,均采用 utf8 字符集,那么字符計(jì)數(shù)方法需要注意芋簿。

說(shuō)明:

SELECT LENGTH("輕松工作")峡懈;--返回為 12
SELECT CHARACTER_LENGTH("輕松工作");--返回為 4

如果需要存儲(chǔ)表情与斤,那么選擇 utf8mb4 來(lái)進(jìn)行存儲(chǔ)肪康,注意它與 utf8 編碼的區(qū)別。

13.【參考】TRUNCATE TABLE 比 DELETE 速度快撩穿,且使用的系統(tǒng)和事務(wù)日志資源少磷支,但 TRUNCATE無(wú)事務(wù)且不觸發(fā) trigger,有可能造成事故食寡,故不建議在開(kāi)發(fā)代碼中使用此語(yǔ)句雾狈。

說(shuō)明:TRUNCATE TABLE 在功能上與不帶 WHERE 子句的 DELETE 語(yǔ)句相同。

(四) ORM映射

7.【強(qiáng)制】更新數(shù)據(jù)表記錄時(shí)抵皱,必須同時(shí)更新記錄對(duì)應(yīng)的 update_time 字段值為當(dāng)前時(shí)間善榛。

8.【推薦】不要寫(xiě)一個(gè)大而全的數(shù)據(jù)更新接口。傳入為 POJO 類(lèi)呻畸,不管是不是自己的目標(biāo)更新字段移盆,都進(jìn)行update table set c1 = value1 , c2 = value2 , c3 = value3;這是不對(duì)的伤为。執(zhí)行 SQL 時(shí)咒循,不要更新無(wú)改動(dòng)的字段,一是易出錯(cuò)绞愚;二是效率低剑鞍;三是增加 binlog 存儲(chǔ)。

9.【參考】@Transactional 事務(wù)不要濫用爽醋。事務(wù)會(huì)影響數(shù)據(jù)庫(kù)的 QPS蚁署,另外使用事務(wù)的地方需要考慮各方面的回滾方案,包括緩存回滾蚂四、搜索引擎回滾光戈、消息補(bǔ)償、統(tǒng)計(jì)修正等遂赠。

10.【參考】<isEqual>中的 compareValue 是與屬性值對(duì)比的常量久妆,一般是數(shù)字,表示相等時(shí)帶上此條件跷睦;<isNotEmpty>表示不為空且不為 null 時(shí)執(zhí)行筷弦;<isNotNull>表示不為 null 值時(shí)執(zhí)行。

六、工程結(jié)構(gòu)

(一) 應(yīng)用分層

1.【推薦】根據(jù)業(yè)務(wù)架構(gòu)實(shí)踐烂琴,結(jié)合業(yè)界分層規(guī)范與流行技術(shù)框架分析爹殊,推薦分層結(jié)構(gòu)如圖所示,默認(rèn)上層依賴(lài)于下層奸绷,箭頭關(guān)系表示可直接依賴(lài)梗夸,如:開(kāi)放 API 層可以依賴(lài)于 Web 層(Controller 層),也可以直接依賴(lài)于 Service 層号醉,依此類(lèi)推:

image

(二) 二方庫(kù)依賴(lài)

11.【推薦】二方庫(kù)不要有配置項(xiàng)反症,最低限度不要再增加配置項(xiàng)。

12.【推薦】不要使用不穩(wěn)定的工具包或者 Utils 類(lèi)畔派。

  • 說(shuō)明:不穩(wěn)定指的是提供方無(wú)法做到向下兼容铅碍,在編譯階段正常,但在運(yùn)行時(shí)產(chǎn)生異常线椰,因此该酗,盡量使用業(yè)界穩(wěn)定的二方工具包。

13.【參考】為避免應(yīng)用二方庫(kù)的依賴(lài)沖突問(wèn)題士嚎,二方庫(kù)發(fā)布者應(yīng)當(dāng)遵循以下原則:

  • 1)精簡(jiǎn)可控原則.移除一切不必要的 API 和依賴(lài),只包含 Service API悔叽、必要的領(lǐng)域模型對(duì)象莱衩、Utils 類(lèi)、常量娇澎、枚舉等笨蚁。如果依賴(lài)其它二方庫(kù),盡量是 provided 引入趟庄,讓二方庫(kù)使用者去依賴(lài)具體版本號(hào)括细;無(wú) log 具體實(shí)現(xiàn),只依賴(lài)日志框架戚啥。
  • 2)稅定可追潮原則.每個(gè)版本的變化應(yīng)該被記錄奋单,二方庫(kù)由誰(shuí)維護(hù),源碼在哪里猫十,都需要能方便查到览濒。除非用戶(hù)主動(dòng)升級(jí)版本,否則公共二方庫(kù)的行為不應(yīng)該發(fā)生變化拖云。

(三) 服務(wù)器

6.【推薦】在線上生產(chǎn)環(huán)境贷笛,JVM 的 Xms 和 Xmx 設(shè)置一樣大小的內(nèi)存容量,避免在 GC 后調(diào)整堆大小帶來(lái)的壓力宙项。

7.【推薦】了解每個(gè)服務(wù)大致的平均耗時(shí)乏苦,可以通過(guò)獨(dú)立配置線程池,將較慢的服務(wù)與主線程池隔離開(kāi)尤筐,免得不同服務(wù)的線程同歸于盡汇荐。

8.【參考】服務(wù)器內(nèi)部重定向必須使用 forward洞就;外部部重定向地址必須使用 URL Broker 生成,否則因線上采用 HTTPS 協(xié)議而導(dǎo)致瀏覽器提示“不安全”拢驾。此外奖磁,還會(huì)帶來(lái) URL 維護(hù)不一致的問(wèn)題。

七繁疤、設(shè)計(jì)規(guī)約

ps:新增11條新規(guī)約咖为!

例如,浮點(diǎn)數(shù)的后綴統(tǒng)一為大寫(xiě)稠腊;枚舉的屬性字段必須是私有且不可變躁染;配置文件中的密碼需要加密等。

新增描述中的正反例 2 條

例如架忌,多個(gè)構(gòu)造方法次序吞彤、NoSuchMethodError 處理;

新增擴(kuò)展說(shuō)明 5 條

例如叹放,父集合元素的增加或刪除異常等饰恕。

修改描述 22 處

例如,魔法值的示例代碼井仰、ScheduledThreadPool 問(wèn)題等埋嵌。

修正嵩山版中部分代碼格式錯(cuò)誤和描述錯(cuò)誤。

image

2022阿里最新版的Java開(kāi)發(fā)手冊(cè)(黃山版)已經(jīng)整理好了俱恶,開(kāi)發(fā)的同學(xué)們趕緊行動(dòng)起來(lái)雹嗦,遵守代碼規(guī)范,你好合是,我好了罪,大家好!

無(wú)規(guī)矩不成方圓 無(wú)規(guī)范不能協(xié)作

眾所周知聪全,制訂交通法規(guī)表面上是要限制行車(chē)權(quán)泊藕,實(shí)際上是保障公眾的人身安全。試想如果沒(méi)有限速难礼,沒(méi)有紅綠燈吱七,沒(méi)有規(guī)定靠右行駛,誰(shuí)還敢上路行駛鹤竭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踊餐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子臀稚,更是在濱河造成了極大的恐慌吝岭,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異窜管,居然都是意外死亡散劫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)幕帆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)获搏,“玉大人,你說(shuō)我怎么就攤上這事失乾〕N酰” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵碱茁,是天一觀的道長(zhǎng)裸卫。 經(jīng)常有香客問(wèn)我,道長(zhǎng)纽竣,這世上最難降的妖魔是什么墓贿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮蜓氨,結(jié)果婚禮上穴吹,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好棘钞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布宜猜。 她就那樣靜靜地躺著,像睡著了一般绅喉。 火紅的嫁衣襯著肌膚如雪叫乌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天革屠,我揣著相機(jī)與錄音似芝,去河邊找鬼。 笑死详炬,一個(gè)胖子當(dāng)著我的面吹牛寞奸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝇闭,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼礼仗!你這毒婦竟也來(lái)了逻悠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤单旁,失蹤者是張志新(化名)和其女友劉穎饥伊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體琅豆,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茫因,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年冻押,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了洛巢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡稿茉,死狀恐怖类垦,靈堂內(nèi)的尸體忽然破棺而出蚤认,到底是詐尸還是另有隱情,我是刑警寧澤砰琢,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布良瞧,位于F島的核電站,受9級(jí)特大地震影響挚冤,放射性物質(zhì)發(fā)生泄漏赞庶。R本人自食惡果不足惜歧强,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摊册。 院中可真熱鬧,春花似錦茅特、人聲如沸忘分。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至熬荆,卻和暖如春舟山,著一層夾襖步出監(jiān)牢的瞬間卤恳,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工寒矿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留突琳,地道東北人符相。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓拆融,卻偏偏與公主長(zhǎng)得像镜豹,于是被迫代替她去往敵國(guó)和親傲须。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容