一蚁吝、編程規(guī)約
(一)命名風(fēng)格
【強(qiáng)制】POJO 類中布爾類型的變量懂版,都不要加 is笋轨,否則部分框架解析會(huì)引起序列化錯(cuò)誤聂沙。
反例:定義為基本數(shù)據(jù)類型 Boolean isDeleted;的屬性瘫寝,它的方法也是 isDeleted()蜒蕾,RPC
框架在反向解析的時(shí)候,“以為”對(duì)應(yīng)的屬性名稱是 deleted焕阿,導(dǎo)致屬性獲取不到咪啡,進(jìn)而拋出異常。
【推薦】如果模塊暮屡、接口撤摸、類、方法使用了設(shè)計(jì)模式褒纲,在命名時(shí)體現(xiàn)出具體模式准夷。
public class OrderFactory;
public class LoginProxy;
public class ResourceObserver;
(二)常量定義
【強(qiáng)制】不允許任何魔法值(即未經(jīng)定義的常量)直接出現(xiàn)在代碼中。
反例:String key = "Id#taobao_" + tradeId;
cache.put(key, value);
【推薦】如果變量值僅在一個(gè)范圍內(nèi)變化莺掠,且?guī)в忻Q之外的延伸屬性衫嵌,定義為枚舉類。
正例:public Enum { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6),
SUNDAY(7);}
(三)代碼格式
(四)OOP 規(guī)約
【強(qiáng)制】避免通過一個(gè)類的對(duì)象引用訪問此類的靜態(tài)變量或靜態(tài)方法彻秆,無(wú)謂增加編譯器解析成本渐扮,直接用類名來訪問即可论悴。
【強(qiáng)制】外部正在調(diào)用或者二方庫(kù)依賴的接口掖棉,不允許修改方法簽名墓律,避免對(duì)接口調(diào)用方產(chǎn)生影響。接口過時(shí)必須加@Deprecated 注解幔亥,并清晰地說明采用的新接口或者新服務(wù)是什么耻讽。
【強(qiáng)制】所有的相同類型的包裝類對(duì)象之間值的比較,全部使用 equals 方法比較帕棉。
說明:對(duì)于 Integer var = ? 在-128 至 127 范圍內(nèi)的賦值针肥,Integer 對(duì)象是在IntegerCache.cache 產(chǎn)生,會(huì)復(fù)用已有對(duì)象香伴,這個(gè)區(qū)間內(nèi)的 Integer值可以直接使用==進(jìn)行判斷慰枕,但是這個(gè)區(qū)間之外的所有數(shù)據(jù),都會(huì)在堆上產(chǎn)生即纲,并不會(huì)復(fù)用已有對(duì)象具帮,這是一個(gè)大坑,推薦使用 equals 方法進(jìn)行判斷低斋。
【強(qiáng)制】序列化類新增屬性時(shí)蜂厅,請(qǐng)不要修改 serialVersionUID 字段,避免反序列失敳渤搿掘猿;如果完全不兼容升級(jí),避免反序列化混亂唇跨,那么請(qǐng)修改 serialVersionUID 值稠通。
說明:注意 serialVersionUID 不一致會(huì)拋出序列化運(yùn)行時(shí)異常。
【強(qiáng)制】構(gòu)造方法里面禁止加入任何業(yè)務(wù)邏輯买猖,如果有初始化邏輯改橘,請(qǐng)放在 init 方法中。
【強(qiáng)制】POJO 類必須寫 toString 方法政勃。使用 IDE 的中工具:source> generate toString
時(shí)唧龄,如果繼承了另一個(gè) POJO 類,注意在前面加一下 super.toString奸远。
說明:在方法執(zhí)行拋出異常時(shí)既棺,可以直接調(diào)用 POJO 的 toString()方法打印其屬性值,便于排
查問題懒叛。
【推薦】循環(huán)體內(nèi)丸冕,字符串的連接方式,使用 StringBuilder 的 append 方法進(jìn)行擴(kuò)展薛窥。
說明:反編譯出的字節(jié)碼文件顯示每次循環(huán)都會(huì) new 出一個(gè) StringBuilder 對(duì)象胖烛,然后進(jìn)行
append 操作眼姐,最后通過 toString 方法返回 String 對(duì)象,造成內(nèi)存資源浪費(fèi)佩番。
反例:
String str ="start";for(int i = 0; i < 100; i++) {str = str +"hello";}
(五)集合處理
【強(qiáng)制】關(guān)于 hashCode 和 equals 的處理众旗,遵循如下規(guī)則:
1) 只要重寫 equals,就必須重寫 hashCode趟畏。
2) 因?yàn)?Set 存儲(chǔ)的是不重復(fù)的對(duì)象贡歧,依據(jù) hashCode 和 equals 進(jìn)行判斷,所以 Set 存儲(chǔ)的對(duì)象必須重寫這兩個(gè)方法赋秀。
3) 如果自定義對(duì)象做為 Map 的鍵利朵,那么必須重寫 hashCode 和 equals。
說明:String 重寫了 hashCode 和 equals 方法猎莲,所以我們可以非常愉快地使用 String 對(duì)象
作為 key 來使用绍弟。
【強(qiáng)制】ArrayList的subList結(jié)果不可強(qiáng)轉(zhuǎn)成ArrayList,否則會(huì)拋出ClassCastException
異常著洼,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.
說明:subList 返回的是 ArrayList 的內(nèi)部類 SubList樟遣,并不是 ArrayList ,而是
ArrayList 的一個(gè)視圖郭脂,對(duì)于 SubList 子列表的所有操作最終會(huì)反映到原列表上年碘。
【強(qiáng)制】使用集合轉(zhuǎn)數(shù)組的方法,必須使用集合的 toArray(T[] array)展鸡,傳入的是類型完全一樣的數(shù)組屿衅,大小就是 list.size()。
反例:直接使用 toArray 無(wú)參方法存在問題莹弊,此方法返回值只能是 Object[]類涤久,若強(qiáng)轉(zhuǎn)其它
類型數(shù)組將出現(xiàn) ClassCastException 錯(cuò)誤。
【強(qiáng)制】使用工具類Arrays.asList()把數(shù)組轉(zhuǎn)換成集合時(shí)忍弛,不能使用其修改集合相關(guān)的方法响迂,它的 add/remove/clear 方法會(huì)拋出 UnsupportedOperationException 異常。
說明:asList 的返回對(duì)象是一個(gè) Arrays 內(nèi)部類细疚,并沒有實(shí)現(xiàn)集合的修改方法蔗彤。Arrays.asList
體現(xiàn)的是適配器模式,只是轉(zhuǎn)換接口疯兼,后臺(tái)的數(shù)據(jù)仍是數(shù)組然遏。
String[] str = new String[] { "you", "wu" };
List list = Arrays.asList(str);
第一種情況:list.add("yangguanbao"); 運(yùn)行時(shí)異常。
第二種情況:str[0] = "gujin"; 那么 list.get(0)也會(huì)隨之修改吧彪。
【強(qiáng)制】不要在 foreach 循環(huán)里進(jìn)行元素的 remove/add 操作待侵。remove 元素請(qǐng)使用 Iterator方式,如果并發(fā)操作姨裸,需要對(duì) Iterator 對(duì)象加鎖秧倾。
【推薦】高度注意 Map 類集合 K/V 能不能存儲(chǔ) null 值的情況怨酝,如下表格:
(六)并發(fā)處理
【強(qiáng)制】線程資源必須通過線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程那先。
說明:使用線程池的好處是減少在創(chuàng)建和銷毀線程上所花的時(shí)間以及系統(tǒng)資源的開銷农猬,解決資
源不足的問題。如果不使用線程池胃榕,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致消耗完內(nèi)存或者
“過度切換”的問題盛险。
【強(qiáng)制】線程池不允許使用 Executors 去創(chuàng)建,而是通過 ThreadPoolExecutor 的方式勋又,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)换帜。
【推薦】避免 Random 實(shí)例被多線程使用楔壤,雖然共享該實(shí)例是線程安全的,但會(huì)因競(jìng)爭(zhēng)同一seed 導(dǎo)致的性能下降惯驼。
說明:Random 實(shí)例包括 java.util.Random 的實(shí)例或者 Math.random()的方式蹲嚣。
正例:在 JDK7 之后,可以直接使用 API ThreadLocalRandom祟牲,而在 JDK7 之前隙畜,需要編碼保
證每個(gè)線程持有一個(gè)實(shí)例。
【參考】volatile 解決多線程內(nèi)存不可見問題说贝。對(duì)于一寫多讀议惰,是可以解決變量同步問題,但是如果多寫乡恕,同樣無(wú)法解決線程安全問題言询。如果是count++操作,使用如下類實(shí)現(xiàn):AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8傲宜,推薦使用 LongAdder 對(duì)象运杭,比 AtomicLong 性能更好(減少樂觀鎖的重試次數(shù))。
(七)控制語(yǔ)句
【推薦】表達(dá)異常的分支時(shí)函卒,少用 if-else 方式
說明:如果非得使用 if()...else if()...else...方式表達(dá)邏輯辆憔,【強(qiáng)制】避免后續(xù)代碼維
護(hù)困難,請(qǐng)勿超過 3 層报嵌。
正例:超過 3 層的 if-else 的邏輯判斷代碼可以使用衛(wèi)語(yǔ)句虱咧、策略模式、狀態(tài)模式等來實(shí)現(xiàn)沪蓬,
其中衛(wèi)語(yǔ)句示例如下:
public voidtoday() {
? ? ?if(isBusy()) {
System.out.println(“change time.”);
? return;
?}
if(isFree()) {
? ? System.out.println(“go to travel.”);return;
?}
? ? ? System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”);
? return;
}
(八)注釋規(guī)約
(九)其它
【強(qiáng)制】在使用正則表達(dá)式時(shí)彤钟,利用好其預(yù)編譯功能,可以有效加快正則匹配速度跷叉。
【強(qiáng)制】后臺(tái)輸送給頁(yè)面的變量必須加$!{var}——中間的感嘆號(hào)逸雹。
說明:如果 var=null 或者不存在营搅,那么${var}會(huì)直接顯示在頁(yè)面上。
【強(qiáng)制】注意 Math.random() 這個(gè)方法返回是 double 類型梆砸,注意取值的范圍 0≤x<1(能夠
取到零值转质,注意除零異常),如果想獲取整數(shù)類型的隨機(jī)數(shù)帖世,不要將 x 放大 10 的若干倍然后
取整休蟹,直接使用 Random 對(duì)象的 nextInt 或者 nextLong 方法。
二日矫、異常日志
(一)異常處理
【強(qiáng)制】Java 類庫(kù)中定義的一類 RuntimeException 可以通過預(yù)先檢查進(jìn)行規(guī)避赂弓,而不應(yīng)該
通過 catch 來處理,比如:IndexOutOfBoundsException哪轿,NullPointerException 等等盈魁。
正例:if (obj != null) {...}
反例:try { obj.method() } catch (NullPointerException e) {...}
【強(qiáng)制】異常不要用來做流程控制,條件控制窃诉,因?yàn)楫惓5奶幚硇时葪l件分支低杨耙。
【強(qiáng)制】對(duì)大段代碼進(jìn)行 try-catch,這是不負(fù)責(zé)任的表現(xiàn)
【強(qiáng)制】捕獲異常是為了處理它飘痛,不要捕獲了卻什么都不處理而拋棄之珊膜,如果不想處理它,請(qǐng)
將該異常拋給它的調(diào)用者宣脉。最外層的業(yè)務(wù)使用者车柠,必須處理異常,將其轉(zhuǎn)化為用戶可以理解的
內(nèi)容脖旱。
【推薦】防止 NPE堪遂,是程序員的基本修養(yǎng).
【參考】在代碼中使用“拋異常”還是“返回錯(cuò)誤碼”萌庆,對(duì)于公司外的 http/api 開放接口必須
使用“錯(cuò)誤碼”溶褪;而應(yīng)用內(nèi)部推薦異常拋出;跨應(yīng)用間 RPC 調(diào)用優(yōu)先考慮使用 Result 方式践险,封
裝 isSuccess()方法猿妈、“錯(cuò)誤碼”、“錯(cuò)誤簡(jiǎn)短信息”
(二)日志規(guī)約
【強(qiáng)制】日志文件推薦至少保存 15 天巍虫,因?yàn)橛行┊惓>邆湟浴爸堋睘轭l次發(fā)生的特點(diǎn)彭则。
【強(qiáng)制】應(yīng)用中不可直接使用日志系統(tǒng)(Log4j、Logback)中的 API占遥,而應(yīng)依賴使用日志框架SLF4J中的API俯抖,使用門面模式的日志框架,有利于維護(hù)和各個(gè)類的日志處理方式統(tǒng)一瓦胎。
三芬萍、單元測(cè)試
【強(qiáng)制】好的單元測(cè)試必須遵守 AIR 原則尤揣。說明:?jiǎn)卧獪y(cè)試在線上運(yùn)行時(shí),感覺像空氣(AIR)一樣并不存在柬祠,但在測(cè)試質(zhì)量的保障上北戏,卻是非常關(guān)鍵的。好的單元測(cè)試宏觀上來說漫蛔,具有自動(dòng)化嗜愈、獨(dú)立性、可重復(fù)執(zhí)行的特點(diǎn)莽龟。
A:Automatic(自動(dòng)化)
I:Independent(獨(dú)立性)
R:Repeatable(可重復(fù))
四蠕嫁、安全規(guī)約
【強(qiáng)制】隸屬于用戶個(gè)人的頁(yè)面或者功能必須進(jìn)行權(quán)限控制校驗(yàn)。
【強(qiáng)制】用戶輸入的 SQL 參數(shù)嚴(yán)格使用參數(shù)綁定或者 METADATA 字段值限定轧房,防止 SQL 注入拌阴,
禁止字符串拼接 SQL 訪問數(shù)據(jù)庫(kù)。
【強(qiáng)制】用戶請(qǐng)求傳入的任何參數(shù)必須做有效性驗(yàn)證奶镶。
五、MySQL 數(shù)據(jù)庫(kù)
(一) 建表規(guī)約
【強(qiáng)制】如果存儲(chǔ)的字符串長(zhǎng)度幾乎相等陪拘,使用 char 定長(zhǎng)字符串類型厂镇。
【強(qiáng)制】表達(dá)是與否概念的字段,必須使用 is _ xxx 的方式命名左刽,數(shù)據(jù)類型是 unsigned tinyint( 1 表示是捺信,0 表示否 )
說明:任何字段如果為非負(fù)數(shù),必須是 unsigned 欠痴。
正例:表達(dá)邏輯刪除的字段名 is_deleted 迄靠,1 表示刪除,0 表示未刪除喇辽。
【強(qiáng)制】 varchar 是可變長(zhǎng)字符串掌挚,不預(yù)先分配存儲(chǔ)空間,長(zhǎng)度不要超過 5000菩咨,如果存儲(chǔ)長(zhǎng)
度大于此值吠式,定義字段類型為 text ,獨(dú)立出來一張表抽米,用主鍵來對(duì)應(yīng)特占,避免影響其它字段索
引效率。
【強(qiáng)制】表必備三字段: id ,? gmt _ create ,? gmt _ modified 云茸。
說明:其中 id 必為主鍵是目,類型為 unsigned bigint 、單表時(shí)自增标捺、步長(zhǎng)為 1懊纳。 gmt_create,
gmt_modified 的類型均為 date_time 類型揉抵,前者現(xiàn)在時(shí)表示主動(dòng)創(chuàng)建,后者過去分詞表示被
動(dòng)更新长踊。
【推薦】字段允許適當(dāng)冗余功舀,以提高查詢性能,但必須考慮數(shù)據(jù)一致身弊。冗余字段應(yīng)遵循:
1 ) 不是頻繁修改的字段辟汰。
2 ) 不是 varchar 超長(zhǎng)字段,更不能是 text 字段阱佛。
正例:商品類目名稱使用頻率高帖汞,字段長(zhǎng)度短,名稱基本一成不變凑术,可在相關(guān)聯(lián)的表中冗余存
儲(chǔ)類目名稱翩蘸,避免關(guān)聯(lián)查詢。
(二) 索引規(guī)約
【強(qiáng)制】業(yè)務(wù)上具有唯一特性的字段淮逊,即使是多個(gè)字段的組合催首,也必須建成唯一索引。
【強(qiáng)制】超過三個(gè)表禁止 join泄鹏。需要 join 的字段郎任,數(shù)據(jù)類型必須絕對(duì)一致;多表關(guān)聯(lián)查詢時(shí)备籽,
保證被關(guān)聯(lián)的字段需要有索引舶治。
【強(qiáng)制】在 varchar 字段上建立索引時(shí),必須指定索引長(zhǎng)度车猬,沒必要對(duì)全字段建立索引霉猛,根據(jù)
實(shí)際文本區(qū)分度決定索引長(zhǎng)度即可。
【強(qiáng)制】頁(yè)面搜索嚴(yán)禁左模糊或者全模糊珠闰,如果需要請(qǐng)走搜索引擎來解決惜浅。
【推薦】利用覆蓋索引來進(jìn)行查詢操作,避免回表铸磅。說明:如果一本書需要知道第 11 章是什么標(biāo)題赡矢,會(huì)翻開第 11 章對(duì)應(yīng)的那一頁(yè)嗎?目錄瀏覽
一下就好阅仔,這個(gè)目錄就是起到覆蓋索引的作用吹散。
正例:能夠建立索引的種類:主鍵索引、唯一索引八酒、普通索引空民,而覆蓋索引是一種查詢的一種
效果,用 explain 的結(jié)果,extra 列會(huì)出現(xiàn):using index界轩。
【推薦】建組合索引的時(shí)候画饥,區(qū)分度最高的在最左邊。
正例:如果 where a=? and b=? 浊猾,a 列的幾乎接近于唯一值抖甘,那么只需要單建 idx_a 索引即
可。
(三) SQL語(yǔ)句
【強(qiáng)制】不要使用 count(列名)或 count(常量)來替代 count(*)葫慎,count(*)是 SQL92 定義的
標(biāo)準(zhǔn)統(tǒng)計(jì)行數(shù)的語(yǔ)法衔彻,跟數(shù)據(jù)庫(kù)無(wú)關(guān),跟 NULL 和非 NULL 無(wú)關(guān)偷办。
【強(qiáng)制】不得使用外鍵與級(jí)聯(lián)艰额,一切外鍵概念必須在應(yīng)用層解決。
說明:以學(xué)生和成績(jī)的關(guān)系為例椒涯,學(xué)生表中的 student _ id 是主鍵柄沮,那么成績(jī)表中的 student _ id則為外鍵。如果更新學(xué)生表中的 student _ id 废岂,同時(shí)觸發(fā)成績(jī)表中的 student _ id 更新祖搓,即為級(jí)聯(lián)更新。外鍵與級(jí)聯(lián)更新適用于單機(jī)低并發(fā)湖苞,不適合分布式棕硫、高并發(fā)集群 ; 級(jí)聯(lián)更新是強(qiáng)阻塞袒啼,存在數(shù)據(jù)庫(kù)更新風(fēng)暴的風(fēng)險(xiǎn) ; 外鍵影響數(shù)據(jù)庫(kù)的插入速度纬纪。
【強(qiáng)制】禁止使用存儲(chǔ)過程蚓再,存儲(chǔ)過程難以調(diào)試和擴(kuò)展,更沒有移植性包各。
(四)ORM 映射
【強(qiáng)制】在表查詢中摘仅,一律不要使用 * 作為查詢的字段列表,需要哪些字段必須明確寫明问畅。
六娃属、工程結(jié)構(gòu)
(一)應(yīng)用分層
(二)二方庫(kù)依賴
(三)服務(wù)器
【推薦】高并發(fā)服務(wù)器建議調(diào)小 TCP 協(xié)議的 time_ wait 超時(shí)時(shí)間。
說明:操作系統(tǒng)默認(rèn) 240 秒后护姆,才會(huì)關(guān)閉處于 time_ wait 狀態(tài)的連接矾端,在高并發(fā)訪問下,服務(wù)器端會(huì)因?yàn)樘幱?time _ wait的連接數(shù)太多卵皂,可能無(wú)法建立新的連接秩铆,所以需要在服務(wù)器上調(diào)小此等待值。
正例:在 linux 服務(wù)器上請(qǐng)通過變更/ etc / sysctl . conf 文件去修改該缺省值 ( 秒 ) :
net . ipv 4. tcp _ fin _ timeout = 30
【推薦】調(diào)大服務(wù)器所支持的最大文件句柄數(shù) (File Descriptor ,簡(jiǎn)寫為 fd) 殴玛。
說明:主流操作系統(tǒng)的設(shè)計(jì)是將 TCP / UDP 連接采用與文件一樣的方式去管理捅膘,即一個(gè)連接對(duì)
應(yīng)于一個(gè) fd 。主流的 linux 服務(wù)器默認(rèn)所支持最大 fd 數(shù)量為 1024滚粟,當(dāng)并發(fā)連接數(shù)很大時(shí)很
容易因?yàn)?fd 不足而出現(xiàn)“ open too many files ”錯(cuò)誤寻仗,導(dǎo)致新的連接無(wú)法建立。 建議將 linux
服務(wù)器所支持的最大句柄數(shù)調(diào)高數(shù)倍 ( 與服務(wù)器的內(nèi)存數(shù)量相關(guān) ) 凡壤。
【推薦】給 JVM 設(shè)置-XX:+HeapDumpOnOutOfMemoryError 參數(shù)署尤,讓 JVM 碰到 OOM 場(chǎng)景時(shí)輸出
dump 信息。
【推薦】在線上生產(chǎn)環(huán)境鲤遥, JVM 的 Xms 和 Xmx 設(shè)置一樣大小的內(nèi)存容量沐寺,避免在 GC 后調(diào)整堆
大小帶來的壓力。
【參考】服務(wù)器內(nèi)部重定向使用 forward盖奈; 外部重定向地址使用 URL 拼裝工具類來生成混坞,否則
會(huì)帶來 URL 維護(hù)不一致的問題和潛在的安全風(fēng)險(xiǎn)。
現(xiàn)在加群:810589193钢坦,點(diǎn)擊鏈接加入群聊【Java架構(gòu)學(xué)習(xí)交流群】:https://jq.qq.com/?_wv=1027&k=5deQUBl獲取Java工程化究孕、高性能及分布式、高性能爹凹、高架構(gòu)厨诸、性能調(diào)優(yōu)、Spring禾酱、MyBatis微酬、Netty源碼分析等多個(gè)知識(shí)點(diǎn)高級(jí)進(jìn)階干貨的直播免費(fèi)學(xué)習(xí)權(quán)限及相關(guān)視頻資料,還有spring和虛擬機(jī)等書籍掃描版
合理利用自己每一分每一秒的時(shí)間來學(xué)習(xí)提升自己颤陶,不要再用"沒有時(shí)間“來掩飾自己思想上的懶惰颗管!趁年輕,使勁拼滓走,給未來的自己一個(gè)交代垦江!