命名規(guī)范
- 類命 駝峰式 MarcoPolo
- 方法名 localValue
- 常量 大寫單詞, 單詞間_分割,語義清楚 MAX _ STOCK _ COUNT
- 抽象類 以Abstract /Base開始,異常類用 Exception結(jié)束,測(cè)試用Test結(jié)尾
- boolean類型,變量不要用is開頭
- 包名統(tǒng)一英文單詞單數(shù)形式,不使用縮寫
- 接口中不加修飾,public 不要加
- 形容能力的接口使用-able結(jié)尾
代碼格式
- 左小括號(hào)/右和字符之間不出現(xiàn)空格,if / for / while / switch / do 等保留字與括號(hào)之間都必須加空格
- 二目、三目運(yùn)算符的左右兩邊都需要加一個(gè)空格
- 第二行相對(duì)第一行縮進(jìn) 4 個(gè)空格,其他不縮進(jìn)
- 傳參要多個(gè)空格隔開
- 不同的業(yè)務(wù)邏輯之間或者不同的語義之間插入一個(gè)空行。相同業(yè)務(wù)邏輯和語義之間不需要插入空行
OOP規(guī)約
訪問類中靜態(tài)方法,不用對(duì)象引用類,直接用類名來進(jìn)行訪問。
過時(shí)接口,@ Deprecated 注解
不能使用過時(shí)的類或方法
常量或確定有值的對(duì)象來調(diào)用equals," test " .equals(object);
包裝類對(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)行判斷。POJO 類屬性必須使用包裝數(shù)據(jù)類型,RPC 方法的返回值和參數(shù)必須使用包裝數(shù)據(jù)類型
所有的局部變量使用基本數(shù)據(jù)類型。
定義 DO / DTO / VO 等 POJO 類時(shí),不要設(shè)定任何屬性默認(rèn)值
POJO 類必須寫 toString 方法。
字符串的連接方式,使用 StringBuilder 的 append 方法進(jìn)行擴(kuò)展。
集合處理
- 只要重寫 equals ,就必須重寫 hashCode 。如果自定義對(duì)象做為 Map 的鍵,那么必須重寫 hashCode 和 equals。
- ArrayList 的 subList 結(jié)果不可強(qiáng)轉(zhuǎn)成 ArrayList ,否則會(huì)拋出 ClassCastException異常,即 java . util . RandomAccessSubList cannot be cast to java . util . ArrayList .
- Arrays . asList() 把數(shù)組轉(zhuǎn)換成集合時(shí),不能使用其修改集合相關(guān)的方法,它的 add / remove / clear 方法會(huì)拋出 UnsupportedOperationException 異常。asList 的返回對(duì)象是一個(gè) Arrays 內(nèi)部類,并沒有實(shí)現(xiàn)集合的修改方法。
- 泛型通配符<? extends T >來接收返回的數(shù)據(jù),此寫法的泛型集合不能使用 add 方法,而 <? super T> 不能使用 get 方法,做為接口調(diào)用賦值時(shí)易出錯(cuò)。第一、頻繁往外讀取內(nèi)容的,適合用<? extends T >。第二、經(jīng)常往里插入的,適合用 <? super T> 。
- 不要在 foreach 循環(huán)里進(jìn)行元素的 remove / add 操作。 remove 元素請(qǐng)使用 Iterator方式,如果并發(fā)操作,需要對(duì) Iterator 對(duì)象加鎖。
- 集合初始化時(shí),指定集合初始值大小。說明: HashMap 使用 HashMap(int initialCapacity) 初始化,
正例:initialCapacity = (需要存儲(chǔ)的元素個(gè)數(shù) / 負(fù)載因子) + 1。注意負(fù)載因子(即 loaderfactor)默認(rèn)為 0.75, 如果暫時(shí)無法確定初始值大小,請(qǐng)?jiān)O(shè)置為 16(即默認(rèn)值)。 - 使用 entrySet 遍歷 Map 類集合 KV ,而不是 keySet 方式進(jìn)行遍歷。而 entrySet 只是遍歷了一次就把 key 和 value 都放到了 entry 中,效率更高。
集合類 | Key | Value | Super | 說明
---|------|------|-----|------|---
Hashtable |不允許為 null | 不允許為 null | Dictionary | 線程安全
ConcurrentHashMap| 不允許為 null| 不允許為 null |AbstractMap | 鎖分段技術(shù)(JDK8:CAS)
TreeMap| 不允許為 null |允許為 null |AbstractMap| 線程不安全
HashMap |允許為 null| 允許為 null |AbstractMap | 線程不安全
ConcurrentHashMap 存儲(chǔ) null 值時(shí)會(huì)拋出 NPE 異常.
并發(fā)處理
- 線程資源必須通過線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程。
- 線程池不允許使用 Executors 去創(chuàng)建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。
- SimpleDateFormat 是線程不安全的類,一般不要定義為 static 變量,如果定義為static ,必須加鎖,或者使用 DateUtils 工具類。
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@ Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
jdk8 可以使用DateTimeFormatter代替simpleDateFormat
多線程并行處理定時(shí)任務(wù)時(shí), Timer 運(yùn)行多個(gè) TimeTask 時(shí),只要其中之一沒有捕獲
拋出的異常,其它任務(wù)便會(huì)自動(dòng)終止運(yùn)行,使用 ScheduledExecutorService 則沒有這個(gè)問題。避免 Random 實(shí)例被多線程使用,雖然共享該實(shí)例是線程安全的,但會(huì)因競(jìng)爭(zhēng)同一
seed 導(dǎo)致的性能下降。在 JDK 7 之后,可以直接使用 API ThreadLocalRandom.在并發(fā)場(chǎng)景下,通過雙重檢查鎖 (double - checked locking) 實(shí)現(xiàn)延遲初始化的優(yōu)
化問題隱患 ( 可參考 The " Double - Checked Locking is Broken " Declaration) ,推薦解
決方案中較為簡(jiǎn)單一種 ( 適用于 JDK 5 及以上版本 ) ,將目標(biāo)屬性聲明為 volatile 型 。volatile 解決多線程內(nèi)存不可見問題。對(duì)于一寫多讀,是可以解決變量同步問題,
但是如果多寫,同樣無法解決線程安全問題。如果是 count ++操作,使用如下類實(shí)現(xiàn):
AtomicInteger count = new AtomicInteger(); count . addAndGet( 1 ); 如果是 JDK 8,推
薦使用 LongAdder 對(duì)象,比 AtomicLong 性能更好 ( 減少樂觀鎖的重試次數(shù) ) 。HashMap 在容量不夠進(jìn)行 resize 時(shí)由于高并發(fā)可能出現(xiàn)死鏈,導(dǎo)致 CPU 飆升,在
開發(fā)過程中可以使用其它數(shù)據(jù)結(jié)構(gòu)或加鎖來規(guī)避此風(fēng)險(xiǎn)。
控制語句
- 表達(dá)異常的分支時(shí),少用 if-else 方式 ,這種方式可以改寫成
if (condition) {
...
return obj;
}
方法的返回值可以為 null ,不強(qiáng)制返回空集合,或者空對(duì)象等,必須添加注釋充分
說明什么情況下會(huì)返回 null 值。調(diào)用方需要進(jìn)行 null 判斷防止 NPE 問題。定義時(shí)區(qū)分 unchecked / checked 異常,避免直接拋出 new RuntimeException() ,
更不允許拋出 Exception 或者 Throwable ,應(yīng)使用有業(yè)務(wù)含義的自定義異常。應(yīng)用中不可直接使用日志系統(tǒng) (Log 4 j 、 Logback) 中的 API ,而應(yīng)依賴使用日志框架
SLF 4 J 中的 API ,使用門面模式的日志框架,有利于維護(hù)和各個(gè)類的日志處理方式統(tǒng)一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);
- 避免重復(fù)打印日志,浪費(fèi)磁盤空間,務(wù)必在 log 4 j . xml 中設(shè)置 additivity = false 。
正例:
<logger name="com.taobao.dubbo.config" additivity="false">
單元測(cè)試
- 單元測(cè)試應(yīng)該是全自動(dòng)執(zhí)行的,并且非交互式的。測(cè)試框架通常是定期執(zhí)行的,執(zhí)行
過程必須完全自動(dòng)化才有意義。輸出結(jié)果需要人工檢查的測(cè)試不是一個(gè)好的單元測(cè)試。單元測(cè)
試中不準(zhǔn)使用 System.out 來進(jìn)行人肉驗(yàn)證,必須使用 assert 來驗(yàn)證.
Mysql
- 表達(dá)是與否概念的字段,必須使用 is _ xxx 的方式命名,數(shù)據(jù)類型是 unsigned tinyint( 1 表示是,0 表示否 )
- 主鍵索引名為 pk_ 字段名;唯一索引名為 uk _字段名 ; 普通索引名則為 idx _字段名。
- 小數(shù)類型為 decimal ,禁止使用 float 和 double 。
- 表必備三字段: id , gmt _ create , gmt _ modified
- 表的命名最好是加上“業(yè)務(wù)名稱_表的作用”
- 超過三個(gè)表禁止 join 。需要 join 的字段,數(shù)據(jù)類型必須絕對(duì)一致 ; 多表關(guān)聯(lián)查詢時(shí),
保證被關(guān)聯(lián)的字段需要有索引。 - 頁面搜索嚴(yán)禁左模糊或者全模糊,如果需要請(qǐng)走搜索引擎來解決。索引文件具有 B - Tree的最左前綴匹配特性,如果左邊的值未確定,那么無法使用此索引。
- 不要使用 count( 列名 ) 或 count( 常量 ) 來替代 count( * ) , count( * ) 是 SQL 92 定義的
標(biāo)準(zhǔn)統(tǒng)計(jì)行數(shù)的語法,跟數(shù)據(jù)庫無關(guān),跟 NULL 和非 NULL 無關(guān)。count( * ) 會(huì)統(tǒng)計(jì)值為 NULL 的行,而 count( 列名 ) 不會(huì)統(tǒng)計(jì)此列為 NULL 值的行。 - count(distinct col) 計(jì)算該列除 NULL 之外的不重復(fù)行數(shù),注意 count(distinct
col 1, col 2 ) 如果其中一列全為 NULL ,那么即使另一列有不同的值,也返回為 0。 - 當(dāng)某一列的值全是 NULL 時(shí), count(col) 的返回結(jié)果為 0,但 sum(col) 的返回結(jié)果為
NULL ,因此使用 sum() 時(shí)需注意 NPE 問題??梢允褂萌缦路绞絹肀苊?sum 的 NPE 問題: SELECT IF(ISNULL(SUM(g)) ,0, SUM(g)) FROM table; - 使用 ISNULL() 來判斷是否為 NULL 值。NULL 與任何值的直接比較都為 NULL。
- 在代碼中寫分頁查詢邏輯時(shí),若 count 為 0 應(yīng)直接返回,避免執(zhí)行后面的分頁語句。
- 不得使用外鍵與級(jí)聯(lián),一切外鍵概念必須在應(yīng)用層解決。
服務(wù)器
- 高并發(fā)的服務(wù)器建議調(diào)小TCP協(xié)議的time_wait超時(shí)時(shí)間。操作系統(tǒng)默認(rèn)是240秒才會(huì)關(guān)閉處于time_wait的鏈接,高并發(fā)下服務(wù)端會(huì)因?yàn)樘幱趖ime_wait連接數(shù)太多,無法建立新連接,需要調(diào)小等待值。
在linux服務(wù)器上通過變更/etc/sysctl.conf修改缺省值
net.ipv4.tcp_fin_timeout = 30
調(diào)大服務(wù)器所支持最大文件的句柄數(shù)。主流操作系統(tǒng)將TCP/UDP連接采用與文件一樣的連接方式管理,一個(gè)連接對(duì)應(yīng)一個(gè)fd.
linux 默認(rèn) fd數(shù)為1024.并發(fā)數(shù)過大會(huì)導(dǎo)致“open too many files”錯(cuò)誤。給JVM設(shè)置-XX:+HeapDumpOnOutOfMemoryError參數(shù),讓JVM碰到OOM輸出Dump
線上JVM的Xms初始堆大小和Xmx最大堆大小一樣存儲(chǔ)容量,避免GC調(diào)整給堆帶來壓力
服務(wù)器內(nèi)重定向使用forward,外部重定向使用URL拼裝工具生成,否則帶來URL維護(hù)不一致問題。
二方庫依賴
線上應(yīng)用不要依賴snapshot版本,不依賴是保證發(fā)布的冪等性。
二方庫的新增或者升級(jí),保持除功能點(diǎn)之外的其他jar包仲裁結(jié)果不變。如果有改變,必須明確評(píng)估和驗(yàn)證,建議進(jìn)行dependency:resolve前后信息對(duì)比,如果仲裁結(jié)果完全不一致,通過dependency:tree找出差異點(diǎn),進(jìn)行excludes排除jar包。
二方庫里可以定義枚舉類型,參數(shù)可以使用枚舉類型,但是接口返回值不允許使用枚舉類型,或包含枚舉類型的pojo
依賴于一個(gè)二方庫時(shí),必須定義一個(gè)統(tǒng)一的版本,避免版本號(hào)的不一致。
應(yīng)用分層
在Dao層,無法用細(xì)粒度異常進(jìn)行catch,所以使用catch(Exception e) 方式,并throw new DAOException(e) 不進(jìn)行打印。
在manager/service層進(jìn)行捕獲,并打印到日志中,service層將日志輸出到磁盤,web層跳轉(zhuǎn)到友好界面。
ORM映射
- 在表進(jìn)行查詢中一律不使用*作為查詢字段列表,需要那些字段必須寫明。
- pojo屬性不能加is,數(shù)據(jù)庫字段必須加is_,需要在mybatis生成器中將代碼進(jìn)行修改。
- sql.xml配置參數(shù)使用 #{},不要使用${}這種方式容易出現(xiàn)SQL注入
- 不允許直接拿HashMap和HashTable作為查詢結(jié)果集的輸出。
- 事務(wù)不要濫用,事務(wù)影響數(shù)據(jù)庫的QPS,使用事務(wù)的地方需要考慮各方面的回滾。
SQL語句
- count(distinct col) 計(jì)算該列除NULL之外的不重復(fù)行,注意count(distinct col1,col2)如果其中一列全為null,即使另一列有不同值也返回0。
- 當(dāng)某一列值全為null,count(col)返回結(jié)果為0,sum(col)返回結(jié)果為NULL,因此Sum(col)要注意NPE問題??梢杂?/li>
select if(isnull(sum(g)),0,sum(g)) from table;
- 使用ISNULL()來判斷是否為NULL值,NULL值與任何值比較都為NULL值。
- 禁止使用存儲(chǔ)過程,存儲(chǔ)過程難以調(diào)試和擴(kuò)展,更沒有移植性。
- 數(shù)據(jù)訂正時(shí),刪除和修改記錄,要先select,避免出現(xiàn)誤刪除,確認(rèn)無誤避免出現(xiàn)誤刪除。
- in操作能避免則避免,實(shí)在不能避免要估計(jì)in后邊集合的數(shù)量,控制在1000個(gè)之內(nèi)。
- 如果有全球化的需要,所有的字符存儲(chǔ)以u(píng)tf-8來進(jìn)行存儲(chǔ),同時(shí)注意
select length("輕松工作");返回12
select character_length("輕松工作"); 返回4
存儲(chǔ)表情用utfmb4來進(jìn)行存儲(chǔ),注意它與utf-8的區(qū)別。
8.不建議使用truncate
索引規(guī)約
- 業(yè)務(wù)上具有唯一特性的字段,即使多個(gè)字段的組合,也必須構(gòu)建唯一索引。
- 在varchar上創(chuàng)建索引,必須指明索引的長(zhǎng)度,沒有必要對(duì)全字段建立索引,根據(jù)實(shí)際文本區(qū)分度決定索引長(zhǎng)度即可。
count(distinct left(列名,索引長(zhǎng)度))/count(*)
- 如果有order by 的場(chǎng)景,請(qǐng)注意利用索引的有序性。order by最后的字段是組合索引的一部分,并且放在索引組合順序的最后,避免出現(xiàn)file_sort的情況,影響查詢性能。
索引 a_b_c where a = ? and b = ? order by c
索引中有范圍查找時(shí),索引的有序性無法利用,where a > 10 order by b; 索引 a_b無法發(fā)生。
利用覆蓋索引來進(jìn)行查詢,避免回表,能夠建立索引的種類:主鍵索引、唯一索引、普通索引、而覆蓋索引是一種查詢的一種效果,用explain的結(jié)果,extra列會(huì)出現(xiàn),using index
利用延遲關(guān)聯(lián)或者子查詢優(yōu)化差多分頁場(chǎng)景。
SQL的性能目標(biāo),至少要達(dá)到range級(jí)別,要求是ref級(jí)別,如果可以是consts最好
consts單表中最多只能有一個(gè)匹配行,在優(yōu)化階段即可讀取到數(shù)據(jù)
ref 指的是使用普通索引
range 對(duì)于索引進(jìn)行范圍檢索建立組合索引的時(shí)候,區(qū)分度最高的在最左面,如果 where a = ? and b = ? a列幾乎接近于唯一值,那么只需要單建idx_a索引即可。
存在非等號(hào)和等號(hào)混合判斷條件時(shí),在創(chuàng)建索引時(shí),請(qǐng)把等號(hào)條件的列前置。
where a > ? and b = ? 即使a的區(qū)分度很高也需要b放在索引最前面。防止字段類型不同所造成的隱式轉(zhuǎn)化,導(dǎo)致索引失效。
創(chuàng)建索引要避免寧濫勿缺,認(rèn)為查詢需要?jiǎng)?chuàng)建一個(gè)索引,寧缺勿濫也不要,認(rèn)為索引會(huì)消耗空間,拖慢更新和新增速度。
抵制唯一索引,認(rèn)為唯一索引需要在應(yīng)用層通過先查后插方式解決。varchar是可變長(zhǎng)字符串,不預(yù)先分配存儲(chǔ)空間,長(zhǎng)度不要超過5000,如果存儲(chǔ)長(zhǎng)度大于此值,定義字段類型為text,獨(dú)立出一張表,用主鍵來對(duì)應(yīng),避免影響其他字段索引效率。
單行表數(shù)據(jù)超過500萬或者單行表容量超過2GB,才推薦進(jìn)行分庫分表。
合適的字符存儲(chǔ)長(zhǎng)度,不但節(jié)約數(shù)據(jù)庫的表空間,節(jié)約索引的存儲(chǔ),更重要的是提升檢索速度。
安全規(guī)約
用戶個(gè)人的頁面必須進(jìn)行權(quán)限校驗(yàn)。
用戶敏感數(shù)據(jù)禁止直接展示,必須脫敏,手機(jī)號(hào)隱藏中間4位。
用戶輸入的sql參數(shù)嚴(yán)格禁止使用參數(shù)綁定或者metadata字段值限定,防止SQL注入,禁止字符串拼接SQL訪問數(shù)據(jù)庫。
用戶請(qǐng)求傳入的參數(shù)必須進(jìn)行有效的驗(yàn)證:否則導(dǎo)致1.page size 過大內(nèi)存溢出 2. 惡意order by 導(dǎo)致數(shù)據(jù)庫查詢慢3.任意重定向 4.SQL注入 5. 反序列化注入 6. 正則輸入源串拒絕服務(wù)ReDos
禁止向HTML頁面輸出未經(jīng)安全過濾或未正確轉(zhuǎn)義的用戶數(shù)據(jù)。
表單、AJAX提交必須執(zhí)行CSRF安全過濾。
CSRF跨站請(qǐng)求偽造是一類常見的編程漏洞,對(duì)于存在CSRF漏洞的應(yīng)用網(wǎng)站,攻擊者可以事先構(gòu)造好URL,只要受害用戶一訪問,后臺(tái)便在用戶不知情的情況下對(duì)數(shù)據(jù)庫進(jìn)行修改。單元測(cè)試可以重復(fù)執(zhí)行,不能受外界環(huán)境的影響,在設(shè)計(jì)時(shí)就要把SUT改為注入,在測(cè)試時(shí)使用spring這樣的DI框架注入一個(gè)本地實(shí)現(xiàn)。
異常處理
java 類庫中定義的一類RuntimeException可以通過預(yù)先檢查進(jìn)行規(guī)避,而不應(yīng)該通過catch進(jìn)行處理,比如IndexOutOfBoundsException,NullPointerException
有try塊放到事務(wù)代碼中,catch后,需要事務(wù)回滾,一定注意手動(dòng)回滾。
不能在finally中使用return,finally塊中的return返回方法后結(jié)束執(zhí)行,不會(huì)再執(zhí)行try中return語句。
方法的返回值可以為null,不強(qiáng)制返回空集合和空對(duì)象,必須添加注釋說明什么情況下返回為空
其他
在使用正則表達(dá)式時(shí)要學(xué)會(huì)利用預(yù)編譯,加快正則匹配速度,定義正則的時(shí)候不要在方法體內(nèi)進(jìn)行定義。
volocity調(diào)用POJO類屬性的時(shí)候,建議直接使用屬性名取值即可,模板引擎會(huì)自動(dòng)按照規(guī)約調(diào)用Pojo的getXxx(),如果是boolean基本數(shù)據(jù)類型調(diào)用 isXxx(),如果Boolean包裝對(duì)象,調(diào)用getXxx()的方法
后臺(tái)輸出給頁面的變量必須加 !
{var} 會(huì)顯示在頁面上。
任何數(shù)據(jù)結(jié)構(gòu)構(gòu)造和初始化,都應(yīng)該指定大小,避免數(shù)據(jù)結(jié)構(gòu)無限增長(zhǎng)吃光內(nèi)存。
對(duì)于暫時(shí)被注釋掉,后續(xù)可能恢復(fù)使用的代碼片段,統(tǒng)一使用///來說明注釋掉代碼的理由。