之前看過阿里內(nèi)部整理的Java開發(fā)手冊文檔旅敷,全篇讀完以后缰趋,整理出一份覺得可能對大家有所幫助的信息~总放。
1.POJO 類中布爾類型的變量哺眯,都不要加 is谷浅,否則部分框架解析會引起序列化錯誤。
反例:定義為基本數(shù)據(jù)類型boolean isSuccess;的屬性,它的方法也是isSuccess()一疯,RPC 框架在反向解析的時候撼玄,“以為”對應的屬性名稱是 success,導致屬性獲取不到违施,進而拋出異常互纯。
2.包名統(tǒng)一使用小寫瑟幕,點分隔符之間有且僅有一個自然語義的英語單詞磕蒲。包名統(tǒng)一使用 單數(shù)形式,但是類名如果有復數(shù)含義只盹,類名可以使用復數(shù)形式辣往。
正例: 應用工具類包名為com.alibaba.open.util、類名為MessageUtils(此規(guī)則參考 spring 的框架結(jié)構(gòu))
3.不要使用一個常量類維護所有常量殖卑,應該按常量功能進行歸類站削,分開維護。如:緩存 相關的常量放在類:CacheConsts 下;系統(tǒng)配置相關的常量放在類:ConfigConsts 下孵稽。
說明:大而全的常量類许起,非得使用查找功能才能定位到修改的常量,不利于理解和維護
4.所有的相同類型的包裝類對象之間值的比較菩鲜,全部使用 equals 方法比較园细。
說明:對于Integer var=?在-128至127之間的賦值,Integer對象是在 IntegerCache.cache 產(chǎn)生接校,會復用已有對象猛频,這個區(qū)間內(nèi)的 Integer 值可以直接使用==進行 判斷,但是這個區(qū)間之外的所有數(shù)據(jù)蛛勉,都會在堆上產(chǎn)生鹿寻,并不會復用已有對象,這是一個大坑诽凌, 推薦使用 equals 方法進行判斷毡熏。
5.所有的POJO類屬性必須使用包裝數(shù)據(jù)類型,RPC方法的返回值和參數(shù)必須使用包裝數(shù)據(jù)類型,所有的局部變量【推薦】使用基本數(shù)據(jù)類型。
6.關于 hashCode 和 equals 的處理侣诵,遵循如下規(guī)則痢法。
只要重寫equals,就必須重寫hashCode窝趣。
因為Set存儲的是不重復的對象疯暑,依據(jù)hashCode和equals進行判斷,所以Set存儲的 對象必須重寫這兩個方法哑舒。
如果自定義對象做為Map的鍵妇拯,那么必須重寫hashCode和equals
String 重寫了 hashCode 和 equals 方法,所以我們可以非常愉快地使用 String 對象 作為 key 來使用。
7.不要在 foreach 循環(huán)里進行元素的 remove/add 操作越锈。remove 元素請使用 Iterator方式仗嗦,如果并發(fā)操作,需要對 Iterator 對象加鎖甘凭。
8.使用 entrySet 遍歷 Map 類集合 KV稀拐,而不是 keySet 方式進行遍歷。
說明:keySet 其實是遍歷了 2 次丹弱,一次是轉(zhuǎn)為 Iterator 對象德撬,另一次是從 hashMap 中取出 key 所對應的 value。而 entrySet 只是遍歷了一次就把 key 和 value 都放到了 entry 中躲胳,效 率更高蜓洪。如果是 JDK8,使用 Map.foreach 方法
9.高度注意 Map 類集合 K/V 能不能存儲 null 值的情況坯苹。
反例: 由于 HashMap 的干擾隆檀,很多人認為 ConcurrentHashMap 是可以置入 null 值,注意存儲 null 值時會拋出 NPE 異常
10.獲取單例對象需要保證線程安全粹湃,其中的方法也要保證線程安全恐仑。
資源驅(qū)動類、工具類为鳄、單例工廠類都需要注意裳仆。
11.線程池不允許使用 Executors 去創(chuàng)建,而是通過 ThreadPoolExecutor 的方式济赎,這樣 的處理方式讓寫的同學更加明確線程池的運行規(guī)則鉴逞,規(guī)避資源耗盡的風險。
Executors 返回的線程池對象的弊端如下
FixedThreadPool 和 SingleThreadPool:允許的請求隊列長度為 Integer.MAX_VALUE司训,可能會堆積大量的請求构捡,從而導致 OOM。
CachedThreadPool 和 ScheduledThreadPool:允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE壳猜,可能會創(chuàng)建大量的線程勾徽,從而導致 OOM。
12.SimpleDateFormat 是線程不安全的類统扳,一般不要定義為static變量喘帚,如果定義為static,必須加鎖咒钟,或者使用 DateUtils 工具類吹由。
正例:注意線程安全,使用 DateUtils朱嘴。亦推薦如下處理:
如果是 JDK8 的應用倾鲫,可以使用 Instant 代替 Date粗合,LocalDateTime 代替 Calendar, DateTimeFormatter 代替 Simpledateformatter乌昔,官方給出的解釋:simple beautiful strong immutable thread-safe隙疚。
13.高并發(fā)時,同步調(diào)用應該去考量鎖的性能損耗磕道。能用無鎖數(shù)據(jù)結(jié)構(gòu)供屉,就不要用鎖;能 鎖區(qū)塊,就不要鎖整個方法體;能用對象鎖溺蕉,就不要用類鎖伶丐。
14.對多個資源、數(shù)據(jù)庫表焙贷、對象同時加鎖時撵割,需要保持一致的加鎖順序贿堰,否則可能會造 成死鎖辙芍。
線程一需要對表 A、B羹与、C 依次全部加鎖后才可以進行更新操作故硅,那么線程二的加鎖順序 也必須是 A、B纵搁、C吃衅,否則可能出現(xiàn)死鎖。
15.并發(fā)修改同一記錄時腾誉,避免更新丟失徘层,要么在應用層加鎖,要么在緩存加鎖利职,要么在 數(shù)據(jù)庫層使用樂觀鎖趣效,使用 version 作為更新依據(jù)。
如果每次訪問沖突概率小于 20%猪贪,推薦使用樂觀鎖跷敬,否則使用悲觀鎖。樂觀鎖的重試次 數(shù)不得小于 3 次热押。
16.多線程并行處理定時任務時西傀,Timer 運行多個 TimeTask 時,只要其中之一沒有捕獲 拋出的異常桶癣,其它任務便會自動終止運行拥褂,使用 ScheduledExecutorService 則沒有這個問題。
17.使用 CountDownLatch 進行異步轉(zhuǎn)同步操作牙寞,每個線程退出前必須調(diào)用 countDown方法饺鹃,線程執(zhí)行代碼注意 catch 異常,確保 countDown 方法可以執(zhí)行,避免主線程無法執(zhí)行 至 countDown 方法尤慰,直到超時才返回結(jié)果馏锡。
說明:注意,子線程拋出異常堆棧伟端,不能在主線程 try-catch 到杯道。
18.避免 Random 實例被多線程使用,雖然共享該實例是線程安全的责蝠,但會因競爭同一 seed 導致的性能下降党巾。
在 JDK7 之后,可以直接使用 API ThreadLocalRandom霜医,在 JDK7 之前齿拂,可以做到每個 線程一個實例。
19.通過雙重檢查鎖(double-checked locking)(在并發(fā)場景)實現(xiàn)延遲初始化的優(yōu) 化問題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問題 解決方案中較為簡單一種(適用于 JDK5 及以上版本)肴敛,將目標屬性聲明為 volatile 型署海。
反例:
20.volatile 解決多線程內(nèi)存不可見問題。對于一寫多讀医男,是可以解決變量同步問題砸狞, 但是如果多寫,同樣無法解決線程安全問題镀梭。如果是 count++操作刀森,使用如下類實現(xiàn)。
AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8报账,推 薦使用 LongAdder 對象研底,比 AtomicLong 性能更好(減少樂觀鎖的重試次數(shù))。
21.HashMap 在容量不夠進行 resize 時由于高并發(fā)可能出現(xiàn)死鏈透罢,導致 CPU 飆升榜晦,在 開發(fā)過程中注意規(guī)避此風險。
22.ThreadLocal 無法解決共享對象的更新問題琐凭,ThreadLocal 對象建議使用 static 修飾芽隆。這個變量是針對一個線程內(nèi)所有操作共有的,所以設置為靜態(tài)變量统屈,所有此類實例共享 此靜態(tài)變量 胚吁,也就是說在類第一次被使用時裝載,只分配一塊存儲空間愁憔,所有此類的對象(只 要是這個線程內(nèi)定義的)都可以操控這個變量腕扶。
23.不能在 finally 塊中使用 return,finally 塊中的 return 返回后方法結(jié)束執(zhí)行吨掌,不 會再執(zhí)行 try 塊中的 return 語句半抱。
24.應用中不可直接使用日志系統(tǒng)(Log4j脓恕、Logback)中的 API,而應依賴使用日志框架SLF4J 中的 API窿侈,使用門面模式的日志框架炼幔,有利于維護和各個類的日志處理方式統(tǒng)一。
25.對 trace/debug/info 級別的日志輸出史简,必須使用條件輸出形式或者使用占位符的方式乃秀。
說明:logger.debug("Processing trade with id: " + id + " symbol: " + symbol); 如果日志級別是 warn,上述日志不會打印圆兵,但是會執(zhí)行字符串拼接操作跺讯,如果 symbol 是對象, 會執(zhí)行 toString()方法殉农,浪費了系統(tǒng)資源刀脏,執(zhí)行了上述操作,最終日志卻沒有打印超凳。
正例:(條件)
正例:(占位符)
26.避免重復打印日志愈污,浪費磁盤空間,務必在 log4j.xml 中設置 additivity=false聪建。
正例:
27.異常信息應該包括兩類信息:案發(fā)現(xiàn)場信息和異常堆棧信息钙畔。如果不處理,那么往上拋金麸。
正例:
28.建表時小數(shù)類型為 decimal,禁止使用 float 和 double簿盅。
float 和 double 在存儲的時候挥下,存在精度損失的問題,很可能在值的比較時桨醋,得到不 正確的結(jié)果棚瘟。如果存儲的數(shù)據(jù)范圍超過 decimal 的范圍,建議將數(shù)據(jù)拆成整數(shù)和小數(shù)分開存儲喜最。
29.varchar 是可變長字符串偎蘸,不預先分配存儲空間,長度不要超過 5000瞬内,如果存儲長 度大于此值迷雪,定義字段類型為 text,獨立出來一張表虫蝶,用主鍵來對應章咧,避免影響其它字段索引效率。
30.如果有 order by 的場景能真,請注意利用索引的有序性赁严。order by 最后的字段是組合 索引的一部分扰柠,并且放在索引組合順序的最后,避免出現(xiàn) file_sort 的情況疼约,影響查詢性能卤档。
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引中有范圍查找,那么索引有序性無法利用程剥,如:WHERE a>10 ORDER BY b; 索引 a_b 無法排序裆装。
31.利用延遲關聯(lián)或者子查詢優(yōu)化超多分頁場景。
說明:MySQL 并不是跳過 offset 行倡缠,而是取 offset+N 行哨免,然后返回放棄前 offset 行,返回 N 行昙沦,那當 offset 特別大的時候琢唾,效率就非常的低下,要么控制返回的總頁數(shù)盾饮,要么對超過 特定閾值的頁數(shù)進行 SQL 改寫采桃。
正例:先快速定位需要獲取的 id 段,然后再關聯(lián):
32.SQL 性能優(yōu)化的目標:至少要達到 range 級別丘损,要求是 ref 級別普办,如果可以是 consts 最好。
1.consts 單表中最多只有一個匹配行(主鍵或者唯一索引)徘钥,在優(yōu)化階段即可讀取到數(shù)據(jù)衔蹲。
2.ref 指的是使用普通的索引(normal index)。
3.range 對索引進行范圍檢索呈础。
反例:explain 表的結(jié)果舆驶,type=index,索引物理文件全掃描而钞,速度非常慢沙廉,這個 index 級 別比較 range 還低,與全表掃描是小巫見大巫
33.建組合索引的時候臼节,區(qū)分度最高的在最左邊撬陵。
如果 where a=? and b=? ,a 列的幾乎接近于唯一值网缝,那么只需要單建 idx_a 索引即 可巨税。
存在非等號和等號混合判斷條件時,在建索引時途凫,請把等號條件的列前置垢夹。如:where a>? and b=? 那么即使 a 的區(qū)分度更高,也必須把 b 放在索引的最前列维费。
34.不要使用 count(列名)或 count(常量)來替代 count(*)果元,count(*)就是 SQL92 定義 的標準統(tǒng)計行數(shù)的語法促王,跟數(shù)據(jù)庫無關,跟 NULL 和非 NULL 無關而晒。
count(*)會統(tǒng)計值為 NULL 的行蝇狼,而 count(列名)不會統(tǒng)計此列為 NULL 值的行。
35.count(distinct col) 計算該列除 NULL 之外的不重復數(shù)量倡怎。注意 count(distinct col1, col2) 如果其中一列全為NULL迅耘,那么即使另一列有不同的值,也返回為0监署。
36.當某一列的值全是 NULL 時颤专,count(col)的返回結(jié)果為 0,但 sum(col)的返回結(jié)果為 NULL钠乏,因此使用 sum()時需注意 NPE 問題栖秕。
正例:可以使用如下方式來避免sum的NPE問題:SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;
37.使用 ISNULL()來判斷是否為 NULL 值。注意:NULL 與任何值的直接比較都為 NULL晓避。
38.不得使用外鍵與級聯(lián)簇捍,一切外鍵概念必須在應用層解決。
學生表中的 student_id 是主鍵俏拱,那么成績表中的 student_id 則為外鍵暑塑。 如果更新學生表中的 student_id,同時觸發(fā)成績表中的 student_id 更新锅必,則為級聯(lián)更新事格。 外鍵與級聯(lián)更新適用于單機低并發(fā),不適合分布式况毅、高并發(fā)集群;級聯(lián)更新是強阻塞分蓖,存在數(shù)據(jù)庫更新風暴的風險;外鍵影響數(shù)據(jù)庫的插入速度。
學習Java的同學注意了6怼!终娃!
學習過程中遇到什么問題或者想獲取學習資源的話味廊,歡迎加入Java學習交流群346942462,我們一起學Java棠耕!