開發(fā)規(guī)約總結(jié)
1.POJO類中的任何布爾類型的變量氧苍,都不要加is前綴让虐,否則部分框架解析會引起序列列化錯誤。
2.接?類中的?方法和屬性不不要加任何修飾符號(public也不不要加)对扶,保持代碼的簡潔性浪南,并加上有效的 javadoc注釋络凿。盡量不要在接口里定義變量絮记,如果一定要定義變量怨愤,確定與接??法相關(guān)憔四,并且是整個應(yīng)?的 基礎(chǔ)常量了赵。
3.枚舉類名帶上Enum后綴柿汛,枚舉成員名稱需要全大寫络断,單詞間?下劃線隔開貌笨。
4.各層命名規(guī)約:
?A) Service/DAO層?方法命名規(guī)約
??1) 獲取單個對象的方法用get作前綴锥惋。
??2) 獲取多個對象的方法用list作前綴,復(fù)數(shù)結(jié)尾遭商,如:listObjects劫流。
??3) 獲取統(tǒng)計值的方法用count作前綴祠汇。
??4) 插?入的?法用save/insert作前綴座哩。
??5) 刪除的?法用remove/delete作前綴。
??6) 修改的?法用update作前綴姜骡。
?B) 領(lǐng)域模型命名規(guī)約
??1) 數(shù)據(jù)對象:xxxDO圈澈,xxx即為數(shù)據(jù)表名康栈。
??2) 數(shù)據(jù)傳輸對象:xxxDTO啥么,xxx為業(yè)務(wù)領(lǐng)域相關(guān)的名稱悬荣。
??3) 展示對象:xxxVO氯迂,xxx?一般為?網(wǎng)?頁名稱嚼蚀。
??4) POJO是DO/DTO/BO/VO的統(tǒng)稱轿曙,禁?止命名成xxxPOJO。
5.不允許任何魔法值(即未經(jīng)預(yù)先定義的常量)直接出現(xiàn)在代碼中皮璧。
反例:String key = "Id#taobao_" + tradeId;
?? cache.put(key, value);
本例中同學(xué)A定義了緩存的key悴务,然后緩存提取的同學(xué)B使?了Id#taobao來提取讯檐,少了下劃線别洪,導(dǎo)致故障挖垛。
6.要使?一個常量類維護所有常量痢毒,要按常量功能進(jìn)行歸類哪替,分開維護凭舶。
正例:緩存相關(guān)的常量放在類CacheConsts下;系統(tǒng)配置相關(guān)的常量放在類ConfigConsts下帅霜。
7.不同邏輯义屏、不同語義闽铐、不同業(yè)務(wù)的代碼之間插?一個空?分隔開來以提升可讀性兄墅。
8.所有整型包裝類對象之間值的比較隙咸,全部使用equals?法比較。
說明:對于Integer var=?在-128?127之間的賦值五督,Integer對象是在IntegerCache.cache產(chǎn)生充包,會復(fù)用已有對象基矮,這個區(qū)間內(nèi)的Integer值可以直接使?==進(jìn)行判斷,但是這個區(qū)間之外的所有數(shù)據(jù)本砰,都會在堆上產(chǎn)生点额,并不會復(fù)用已有對象咖楣,這是?個?坑,推薦使用equals?法進(jìn)?判斷咕缎。
9.浮點數(shù)之間的等值判斷凭豪,基本數(shù)據(jù)類型不能用==來比較晒杈,包裝數(shù)據(jù)類型不能用equals來判斷拯钻。
因為浮點數(shù)采?用“尾數(shù)+階碼”的編碼?方式粪般,二進(jìn)制?法精確表示大部分的十進(jìn)制小數(shù)
float精度計算原理:鏈接
正例:
(1) 指定?一個誤差范圍亩歹,兩個浮點數(shù)的差值在此范圍之內(nèi),則認(rèn)為是相等的
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
float diff = 1e-6f;
if (Math.abs(a - b) < diff) {
System.out.println("true");
}
(2) 使?用BigDecimal來定義值稼钩,再進(jìn)?行行浮點數(shù)的運算操作
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
if (x.equals(y)) {
System.out.println("true");
}
10.禁?使用構(gòu)造方法 BigDecimal(double) 的方式把 double值 轉(zhuǎn)化為 BigDecimal對象
說明:BigDecimal(double)存在精度損失風(fēng)險变抽,在精確計算或值比較的場景中可能會導(dǎo)致業(yè)務(wù)邏輯異常绍载。
如:BigDecimal g = new BigDecimal(0.1f); 實際的存儲值 為:0.100000001490116119384765625
優(yōu)先推薦入?yún)镾tring的構(gòu)造方法击儡,或使用BigDecimal的valueOf?方法阳谍,此方法內(nèi)部其實執(zhí)行了Double的 toString矫夯,而Double的toString按double的實際能表達(dá)的精度對尾數(shù)進(jìn)行了截斷训貌。
BigDecimal good1 = new BigDecimal("0.1");
BigDecimal good2 = BigDecimal.valueOf(0.1);
11.使用索引訪問用String的split?法得到的數(shù)組時冒窍,需做最后一個分隔符后有?內(nèi)容的檢查,否則會有拋IndexOutOfBoundsException的風(fēng)險款慨。
說明:
String str = "a,b,c,,";
String[] ary = str.split(",");
// 預(yù)期?大于3檩奠,結(jié)果是3
System.out.println(ary.length);
12.循環(huán)體內(nèi)埠戳,字符串的聯(lián)接?式士葫,使用StringBuilder的append方法進(jìn)?行擴展慢显。
反例:
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
說明:反編譯出的字節(jié)碼文件顯示每次循環(huán)都會new出一個StringBuilder對象,然后進(jìn)行append操作屋灌, 最后通過toString方法返回String對象共郭,造成內(nèi)存資源浪費。
String和StringBuilder對比原理:鏈接
13.慎?Object的clone?法來拷貝對象写半。
說明:對象的clone方法默認(rèn)是淺拷貝叠蝇,若想實現(xiàn)深拷貝需要重寫clone方法實現(xiàn)域?qū)ο蟮纳疃缺闅v式拷?悔捶。
clone拷貝:鏈接
14.Collections類返回的對象蜕该,如:emptyList()/singletonList()等都是immutable list堂淡,不可對其進(jìn)行添加或者 刪除元素的操作淤齐。
反例:某??庫的?法中,如果查詢無結(jié)果居灯,返回Collections.emptyList()空集合對象怪嫌,調(diào)?方?旦進(jìn)行了添加 元素的操作岩灭,就會觸發(fā)UnsupportedOperationException異常噪径。
15.ArrayList的subList結(jié)果不可強轉(zhuǎn)成ArrayList找爱,否則會拋出ClassCastException異常: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ;
說明: subList 返回的是 ArrayList 的內(nèi)部類 SubList寺谤,并不是 ArrayList 吮播,?是 ArrayList 的一個視圖意狠,對于 SubList子列表的所有操作最終會反映到原列表上摄职。
16.在subList場景中蛔垢,?度注意對父集合元素的增加或刪除迫悠,均會導(dǎo)致子列表的遍歷艺玲、增加饭聚、刪除產(chǎn)?ConcurrentModification Exception 異常秒梳。
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
List<Integer> ll = list.subList(0, 3);
list.add(5);
//會拋出ConcurrentModificationException異常
System.out.println(ll.size());
17.使?集合轉(zhuǎn)數(shù)組的方法,必須使用集合的toArray(T[] array)兴垦,傳入的是類型完全一樣的數(shù)組探越,大?就是 list.size()阴汇。
反例:
直接使?用toArray?無參?方法存在問題搀庶,此?方法返回值只能是Object[]類,若強轉(zhuǎn)其它類型數(shù)組將出現(xiàn)ClassCastException錯誤铜异。
正例:
List<String> list = new ArrayList<>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
18.在使用Collection接口任何實現(xiàn)類的addAll()方法時哥倔,都要對輸入的集合參數(shù)進(jìn)行NPE判斷。
說明:在ArrayList.addAll?法的第??代碼即 Object[] a = c.toArray();
其中c為輸?集合參數(shù)揍庄,如果為null咆蒿,則直接拋出異常。
19.使??具類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[] { "a", "b" };
List list = Arrays.asList(str);
第一種情況:list.add("c"); 運行時異常。
第二種情況:str[0]= "changed"; 那么list.get(0)也會隨之修改,反之亦然。
20.不要在foreach循環(huán)里進(jìn)行元素的remove/add操作。remove元素請使用Iterator?式,如果并發(fā)操作慌盯,需要對Iterator對象加鎖孕讳。
反例:
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
說明:把上面代碼中的"1".equals(item)換成"2"會拋出ConcurrentModificationException異常
拋出異常原因和foreach原理:鏈接
正例:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (刪除元素的條件) {
iterator.remove();
}
}
21.在JDK7版本以上,Comparator要滿?如下三個條件,不然Arrays.sort,Collections.sort會拋IllegalArgumentException異常。
說明:
?1) x呻畸,y的?比??結(jié)果和y据途,x的?比??結(jié)果相反便脊。
?2) x>y,y>z,則x>z。<br />
?3) x=y,則x,z?比??結(jié)果和y铅碍,z?比??結(jié)果相同士嚎。
JDK7/JDK8的Collections.Sort,Arrays.sort方法實現(xiàn)中睹晒,如果兩個值是相等的锉试,那么compare方法需要返回0应又,否則可能會在排序時拋錯盆繁,而JDK6是沒有這個限制的秕狰。
反例:下例例中沒有處理理相等的情況吞彤,實際使?用中可能會出現(xiàn)異常:
new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() > o2.getId() ? 1 : -1;
} }
22.集合初始化時,指定集合初始值?小锭环。
說明:如果暫時?無法確定集合?大?小,那么指定默認(rèn)值(16)即可:
反例:HashMap需要放置1024個元素,由于沒有設(shè)置容量初始?小吧寺,隨著元素不斷增加纬乍,容量7次被迫擴?蜓氨,
需要重建hash表,嚴(yán)重影響性能姨拥。
23.表達(dá)異常分支時,少?if-else方式盐类,這種?式可以改寫成:
if (condition) {
...
return obj;
}
// 接著寫else的業(yè)務(wù)邏輯代碼;
說明:如果非得使用if()...else if()...else...?式表達(dá)邏輯逻悠,
【強制】請勿超過3層,超過請使?用狀態(tài)設(shè)計模式冻押。
正例:超過3層的 if-else 的邏輯判斷代碼可以使?衛(wèi)語句、策略模式蘸嘶、狀態(tài)模式等來實現(xiàn)训挡,其中衛(wèi)語句 示例如下:
public void today() {
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;
}
24.避免?ApacheBeanutils進(jìn)行屬性的copy。
說明:Apache BeanUtils性能差,可以使?其他方案?如Spring BeanUtils, Cglib BeanCopier, 注意均是淺拷貝绸狐。
25.獲取當(dāng)前毫秒數(shù):System.currentTimeMillis();
?不是new Date().getTime();
說明:如果想獲取更更加精確的納秒級時間值,使?用System.nanoTime的?方式蓝牲。
在JDK8中哼蛆,針對統(tǒng)計時間等場景,推薦使用Instant類靴迫。
26.防?NPE惕味,是程序員的基本修養(yǎng),注意NPE產(chǎn)?生的場景:
?1) 返回類型為基本數(shù)據(jù)類型玉锌,return包裝數(shù)據(jù)類型的對象時名挥,?自動拆箱有可能產(chǎn)?生NPE。
反例:public int f(){ return Integer對象}主守,如果為null禀倔,?自動解箱拋NPE。
?2) 數(shù)據(jù)庫的查詢結(jié)果可能為null参淫。
?3) 集合里的元素即使isNotEmpty救湖,取出的數(shù)據(jù)元素也可能為null。
?4) 遠(yuǎn)程調(diào)?返回對象時涎才,?律要求空指針判斷鞋既,防止NPE。
?5) 對于session中獲取的數(shù)據(jù)耍铜,建議進(jìn)行NPE檢查邑闺,避免空指針。
?6) 級聯(lián)調(diào)?obj.getA().getB().getC();易產(chǎn)?生NPE棕兼。
27.應(yīng)?中不可直接使用日志系統(tǒng)(Log4j陡舅、Logback)中的API,而應(yīng)依賴使用日志框架(SLF4J程储、JCL--Jakarta Commons Logging)中的API蹭沛。什么是日志框架和日志系統(tǒng),請參考webx作者寶寶的?文章
(鏈接)章鲤,文章里 也詳細(xì)說明了為什么不能直接依賴使用?志系統(tǒng)?是?志框架摊灭,以及應(yīng)用的pom中如何做 dependencyManagement。
說明:?日志框架(SLF4J败徊、JCL--Jakarta Commons Logging)的使?用?方式(推薦使?用SLF4J)
28.日志?件推薦?少保存15天帚呼,因為有些異常具備以“周”為頻次發(fā)生的特點。對于當(dāng)天日志,以“應(yīng)?用 名.log”來保存煤杀,保存在/home/admin/應(yīng)?用名/logs/?目錄下眷蜈,過往?日志格式為: {logname}.log.{保存 ?日期},?日期格式:yyyy-MM-dd
說明:以mppserver應(yīng)?用為例例沈自,日志保存在/home/admin/mppserver/logs/mppserver.log酌儒,歷史?日志 名稱為
mppserver.log.2016-08-01
29.在日志輸出時,字符串變量之間的拼接使用占位符的?式枯途。
說明:因為String字符串的拼接會使用StringBuilder的append()方式忌怎,有一定的性能損耗。使用占位符僅是替換 動作酪夷,可以有效提升性能榴啸。
正例:
logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
30.表達(dá)是與否概念的字段,必須使用is_xxx的?式命名晚岭,數(shù)據(jù)類型是unsigned tinyint(1表示是鸥印,0表示否),
此規(guī)則同樣適?用于odps建表坦报。
說明:任何字段如果為?負(fù)數(shù)库说,必須是unsigned。
注意:POJO類中的任何布爾類型的變量量片择,都不不要加is前綴璃弄,所以,需要在<resultMap>設(shè)置從is_xxx到 Xxx的映射關(guān)系构回。數(shù)據(jù)庫表示是與否的值,使?用tinyint類型疏咐,堅持is_xxx的命名?方式是為了了明確其取值含義 與取值范圍纤掸。
正例:表達(dá)邏輯刪除的字段名 is_deleted ,1表示刪除浑塞,0表示未刪除借跪。
31.表名、字段名必須使用?寫字母或數(shù)字酌壕,字段命名可參考附2;禁止出現(xiàn)數(shù)字開頭掏愁,禁?兩個下劃線中間只
出現(xiàn)數(shù)字。數(shù)據(jù)庫字段名的修改代價很大卵牍,因為無法進(jìn)行預(yù)發(fā)布果港,所以字段名稱需要慎重考慮。
說明:MySQL在Windows下不區(qū)分大小寫糊昙,但在Linux下默認(rèn)是區(qū)分?小寫辛掠。因此,數(shù)據(jù)庫名、表名萝衩、字段名回挽, 都不允許出現(xiàn)任何?寫字母,避免節(jié)外?枝猩谊。
正例:getter_admin千劈,task_config,level3_name
32.禁?保留字牌捷,如desc墙牌、range、match宜鸯、delayed等憔古,參考官方保留字 鏈接
33.唯一索引名為uk_字段名;普通索引名則為idx_字段名。
說明:uk_ 即 unique key;idx_ 即index的簡稱淋袖。
索引知識點:鏈接
explain使用字段說明:鏈接
加深最左原則理解:鏈接
不能用到索引情況:鏈接
34.小數(shù)類型為decimal鸿市,禁止使用float和double。
說明:float和double在存儲的時候即碗,存在精度損失的問題焰情,很可能在值的比較時,得到不正確的結(jié)果剥懒。如果 存儲的數(shù)據(jù)范圍超過decimal的范圍内舟,建議將數(shù)據(jù)拆成整數(shù)和小數(shù)分開存儲。
35.varchar是可變長字符串初橘,不預(yù)先分配存儲空間验游,長度不要超過5000,如果存儲長度大于此值保檐,定義字段類 型為TEXT耕蝉,獨?出來一張表,用主鍵來對應(yīng)夜只,避免影響其它字段索引效率垒在。
36.業(yè)務(wù)上具有唯一特性的字段,即使是組合字段扔亥,也必須建成唯一索引场躯。
說明:不要以為唯一索引影響了insert速度,這個速度損耗可以忽略旅挤,但提高查找速度是明顯的;另外踢关,即使在應(yīng) ?層做了非常完善的校驗和控制,只要沒有唯一索引粘茄,根據(jù)?菲定律耘成,必然有臟數(shù)據(jù)產(chǎn)生。
37.超過三個表禁止join。需要join的字段瘪菌,數(shù)據(jù)類型保持絕對一致;多表關(guān)聯(lián)查詢時撒会,保證被關(guān)聯(lián)的字段需要 有索引。
38.在varchar字段上建立索引時师妙,必須指定索引長度诵肛,沒必要對全字段建立索引,根據(jù)實際文本區(qū)分度決定索 引?度默穴。
說明:索引的長度與區(qū)分度是一對矛盾體怔檩,一般對字符串類型數(shù)據(jù),長度為20的索引蓄诽,區(qū)分度會高達(dá)90%以 上薛训,可以使用count(distinct left(列列名, 索引?長度))/count(*)的區(qū)分度來確定。0.31為黃金值
39.如果有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?法排序。
40.利用覆蓋索引來進(jìn)行查詢操作捶牢,來避免回表操作鸠珠。
說明:如果一本書需要知道第11章是什么標(biāo)題,會翻開第11章對應(yīng)的那??嗎?目錄瀏覽一下就好秋麸,這個目錄就是起到覆蓋索引的作用跳芳。如果索引包含所有滿足查詢需要的數(shù)據(jù)的索引成為覆蓋索引(Covering Index),也就是 平時所說的不需要回表操作<br />正例:IDB能夠建?索引的種類分為【主鍵索引竹勉、唯一索引、普通索引】娄琉,而覆蓋索引是一種查詢的一種效果次乓, ? explain的結(jié)果,extra列列會出現(xiàn):using index.
41.利用延遲關(guān)聯(lián)或者子查詢優(yōu)化超多分頁場景孽水。
說明:MySQL并不是跳過offset行票腰,而是取offset+N行,然后返回放棄前offset行女气,返回N行杏慰,那當(dāng)offset特別 大的時候,效率就?常的低下,要么控制返回的總?數(shù)缘滥,要么對超過特定閾值的頁數(shù)進(jìn)行SQL改寫轰胁。
正例:先快速定位需要獲取的id段,然后再關(guān)聯(lián):
SELECT a.* FROM 表1 a, (select id from 表1 where 條件 LIMIT 100000,20 ) b where a.id=b.id
42.建組合索引的時候朝扼,區(qū)分度最高的在最左邊赃阀。
正例:如果where a=? and b=? ,a列的幾乎接近于唯一值擎颖,那么只需要單建idx_a索引即可榛斯。
說明:存在非等號和等號混合判斷條件時,在建索引時搂捧,請把等號條件的列前置驮俗。
如:where c>? and d=? 那么即使c的區(qū)分度更高,也必須把d放在索引的最前列允跑,即建立組合索引idx_d_c王凑。
43.不要使用count(列名)或count(常量)來替代count(),count()就是SQL92定義的標(biāo)準(zhǔn)統(tǒng)計行數(shù)的語法吮蛹,跟 數(shù)據(jù)庫?關(guān)荤崇,跟NULL和?NULL?關(guān)。
說明:count(*)會統(tǒng)計值為NULL的行潮针,?count(列名)不會統(tǒng)計此列為NULL值的?术荤。
44.count(distinct col) 計算該列除NULL之外的不重復(fù)數(shù)量。
注意:count(distinct col1, col2) 如果其中一列全為NULL每篷,那么即使另一列有不同的值瓣戚,也返回為0。
45.當(dāng)某一列的值全是NULL時焦读,count(col)的返回結(jié)果為0子库,但sum(col)的返回結(jié)果為NULL,因此使用sum() 時需注意NPE問題矗晃。
正例:可以使用如下?式來避免sum的NPE問題:SELECT IFNULL(SUM(column), 0) FROM table;
46.使用ISNULL()來判斷是否為NULL值仑嗅。
?說明: NULL 與任何值的直接比較都為 NULL 。
??1) NULL<>NULL 的返回結(jié)果是 NULL 张症,?不是 false 仓技。
??2) NULL=NULL 的返回結(jié)果是 NULL ,?不是 true 俗他。
?? 3) NULL<>1 的返回結(jié)果是 NULL 脖捻,?不是 true 。
47.在代碼中寫分頁查詢邏輯時兆衅,若count為0應(yīng)直接返回地沮,避免執(zhí)行后面的分?語句嗜浮。
48.不要用resultClass當(dāng)返回參數(shù),即使所有類屬性名與數(shù)據(jù)庫字段?一對應(yīng)摩疑,也需要定義;反過來危融,每一個表 也必然有一個與之對應(yīng)。
說明:配置映射關(guān)系未荒,使字段與DO類解耦专挪,?方便便維護。
49.工具類二?庫已經(jīng)提供的片排,不要在本應(yīng)?中編程實現(xiàn)寨腔。
json操作: fastjson md5操作:commons-codec
?具集合:Guava包
數(shù)組操作:ArrayUtils(org.apache.commons.lang3.ArrayUtils)
集合操作:CollectionUtils(org.apache.commons.collections4.CollectionUtils)
除上面以外還有NumberUtils、DateFormatUtils率寡、DateUtils等優(yōu)先使?用org.apache.commons.lang3 這個包下的迫卢,不要使用org.apache.commons.lang包下?的。
原因是commons.lang這個包是從JDK1.2 開始支持的所以很多1.5/1.6的特性是不?持的冶共,例如:泛型乾蛤。