旅游輿情監(jiān)測編碼規(guī)范_第1頁
旅游輿情監(jiān)測編碼規(guī)范_第2頁
旅游輿情監(jiān)測編碼規(guī)范_第3頁
旅游輿情監(jiān)測編碼規(guī)范_第4頁
旅游輿情監(jiān)測編碼規(guī)范_第5頁
已閱讀5頁,還剩68頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

旅游輿情監(jiān)測項(xiàng)目編碼規(guī)范XX科技股份有限公司編制目錄一、編程規(guī)約 4(一) 命名風(fēng)格 4(二) 常量定義 8 代碼格式 9 13 集合處理 19 并發(fā)處理 24 控制語句 30 注釋規(guī)約 34 其它 35二、異常日志 37 異常處理 37 日志規(guī)約 40三、單元測試 42四、安全規(guī)約 44五、MySQL數(shù)據(jù)庫 45建表規(guī)約 45索引規(guī)約 47SQL語句 49ORM映射 50六、工程結(jié)構(gòu) 51應(yīng)用分層 52二方庫依賴 53(三)服務(wù)器 55七、設(shè)計(jì)規(guī)約 56附1:專有名詞解釋 60PAGEPAGE19一、編程規(guī)約(一) 命名風(fēng)格1.【強(qiáng)制】代碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結(jié)束。反例:_name/name/$name/name_/name$/name 2.【強(qiáng)制】代碼中的命名嚴(yán)禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。說明:正確的英文拼寫和語法可以讓閱讀者易于理解,避免歧義。注意,純拼音命名方式更要避免采用。正例:renminbi/alibaba/taobao/youku/hangzhou等國際通用的名稱,可視同英文。反例:DaZhePromotion[打折]/getPingfenByName()[評分]/int某變量=3【強(qiáng)制】類名使用UpperCamelCase風(fēng)格,但以下情形例外:DO/BO/DTO/VO/AO/PO/UID等。正例:JavaServerlessPlatform/UserDO/XmlService/TcpUdpDeal/TaPromotion反例:javaserverlessplatform/UserDo/XMLService/TCPUDPDeal/TAPromotion4.【強(qiáng)制】方法名、參數(shù)名、成員變量、局部變量都統(tǒng)一使用lowerCamelCase風(fēng)格,必須遵從駝峰形式。正例:localValue/getHttpMessage()/inputUserId5.【強(qiáng)制】常量命名全部大寫,單詞間用下劃線隔開,力求語義表達(dá)完整清楚,不要嫌名字長。正例:MAX_STOCK_COUNT/CACHE_EXPIRED_TIME反例:MAX_COUNT/EXPIRED_TIME6.【強(qiáng)制】抽象類命名使用Abstract或Base開頭;異常類命名使用Exception結(jié)尾;測試類命名以它要測試的類的名稱開始,以Test結(jié)尾。7.【強(qiáng)制】類型與中括號緊挨相連來表示數(shù)組。正例:定義整形數(shù)組int[]arrayDemo;反例:在main參數(shù)中,使用Stringargs[]來定義。8.【強(qiáng)制】POJO類中布爾類型變量都不要加is前綴,否則部分框架解析會引起序列化錯(cuò)誤。說明:在本文MySQL規(guī)約中的建表約定第一條,表達(dá)是與否的值采用is_xxx的命名方式,所以,需要在<resultMap>設(shè)置從is_xxx到xxx的映射關(guān)系。反例:定義為基本數(shù)據(jù)類型BooleanisDeleted的屬性,它的方法也是isDeleted(),RPC框架在反向解析的時(shí)候,“誤以為”對應(yīng)的屬性名稱是deleted,導(dǎo)致屬性獲取不到,進(jìn)而拋出異常。9.【強(qiáng)制】包名統(tǒng)一使用小寫,點(diǎn)分隔符之間有且僅有一個(gè)自然語義的英語單詞。包名統(tǒng)一使用單數(shù)形式,但是類名如果有復(fù)數(shù)含義,類名可以使用復(fù)數(shù)形式。正例:應(yīng)用工具類包名為com.alibaba.ai.util、類名為MessageUtils(此規(guī)則參考spring的框架結(jié)構(gòu))10.【強(qiáng)制】避免在子父類的成員變量之間、或者不同代碼塊的局部變量之間采用完全相同的命名,使可讀性降低。說明:子類、父類成員變量名相同,即使是public類型的變量也是能夠通過編譯,而局部變量在同一方法內(nèi)的不同代碼塊中同名也是合法的,但是要避免使用。對于非setter/getter的參數(shù)名稱也要避免與成員變量名稱相同。反例:publicclassConfusingName{publicintage;//非setter/getter的參數(shù)名稱,不允許與本類成員變量同名publicvoidgetData(Stringalibaba){if(condition){finalintmoney=531;//...}for(inti=0;i<10;i++){//在同一方法體中,不允許與其它代碼塊中的money命名相同finalintmoney=615;//...}}}classSonextendsConfusingName{//不允許與父類的成員變量名稱相同publicintage;}11.【強(qiáng)制】杜絕完全不規(guī)范的縮寫,避免望文不知義。反例:AbstractClass“縮寫”命名成AbsClass;condition“縮寫”命名成condi,此類隨意縮寫嚴(yán)重降低了代碼的可閱讀性。12.【推薦】為了達(dá)到代碼自解釋的目標(biāo),任何自定義編程元素在命名時(shí),使用盡量完整的單詞組合來表達(dá)其意。正例:在JDK中,表達(dá)原子更新的類名為:AtomicReferenceFieldUpdater。反例:inta的隨意命名方式。13.【推薦】在常量與變量的命名時(shí),表示類型的名詞放在詞尾,以提升辨識度。正例:startTime/workQueue/nameList/TERMINATED_THREAD_COUNT反例:startedAt/QueueOfWork/listName/COUNT_TERMINATED_THREAD14.【推薦】如果模塊、接口、類、方法使用了設(shè)計(jì)模式,在命名時(shí)需體現(xiàn)出具體模式。說明:將設(shè)計(jì)模式體現(xiàn)在名字中,有利于閱讀者快速理解架構(gòu)設(shè)計(jì)理念正例:publicclassOrderFactory;publicclassLoginProxy;publicclassResourceObserver;15.【推薦】接口類中的方法和屬性不要加任何修飾符號(public也不要加),保持代碼的簡潔性,并加上有效的Javadoc注釋。盡量不要在接口里定義變量,如果一定要定義變量,肯定是與接口方法相關(guān),并且是整個(gè)應(yīng)用的基礎(chǔ)常量。正例:接口方法簽名voidcommit();接口基礎(chǔ)常量StringCOMPANY="alibaba";反例:接口方法定義publicabstractvoidf();說明:JDK8中接口允許有默認(rèn)實(shí)現(xiàn),那么這個(gè)default方法,是對所有實(shí)現(xiàn)類都有價(jià)值的默認(rèn)實(shí)現(xiàn)。16.接口和實(shí)現(xiàn)類的命名有兩套規(guī)則:1)【強(qiáng)制】對于Service和DAO類,基于SOA的理念,暴露出來的服務(wù)一定是接口,內(nèi)部的實(shí)現(xiàn)類用Impl的后綴與接口區(qū)別。正例:CacheServiceImpl實(shí)現(xiàn)CacheService接口。2)【推薦】如果是形容能力的接口名稱,取對應(yīng)的形容詞為接口名(通常是–able的形容詞)。正例:AbstractTranslator實(shí)現(xiàn)Translatable接口。17.【參考】枚舉類名帶上Enum后綴,枚舉成員名稱需要全大寫,單詞間用下劃線隔開。說明:枚舉其實(shí)就是特殊的類,域成員均為常量,且構(gòu)造方法被默認(rèn)強(qiáng)制是私有。正例:枚舉名字為ProcessStatusEnum的成員名稱:SUCCESS/UNKNOWN_REASON。18.【參考】各層命名規(guī)約:Service/DAO層方法命名規(guī)約1)獲取單個(gè)對象的方法用get做前綴。2)獲取多個(gè)對象的方法用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ù)對象: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。(二)常量定義1.【強(qiáng)制】不允許任何魔法值(即未經(jīng)預(yù)先定義的常量)直接出現(xiàn)在代碼中。反例:Stringkey="Id#taobao_"+tradeId;cache.put(key,value);//緩存get時(shí),由于在代碼復(fù)制時(shí),漏掉下劃線,導(dǎo)致緩存擊穿而出現(xiàn)問題2.【強(qiáng)制】在long或者Long賦值時(shí),數(shù)值后使用大寫的L,不能是小寫的l,小寫容易跟數(shù)字1混淆,造成誤解。說明:Longa=2l;寫的是數(shù)字的21,還是Long2。3.【推薦】不要使用一個(gè)常量類維護(hù)所有常量,要按常量功能進(jìn)行歸類,分開維護(hù)。說明:大而全的常量類,雜亂無章,使用查找功能才能定位到修改的常量,不利于理解和維護(hù)。正例:緩存相關(guān)常量放在類CacheConsts下;系統(tǒng)配置相關(guān)常量放在類ConfigConsts下。4.【推薦】常量的復(fù)用層次有五層:跨應(yīng)用共享常量、應(yīng)用內(nèi)共享常量、子工程內(nèi)共享常量、包內(nèi)共享常量、類內(nèi)共享常量。1)跨應(yīng)用共享常量:放置在二方庫中,通常是client.jar中的constant目錄下。2)應(yīng)用內(nèi)共享常量:放置在一方庫中,通常是子模塊中的constant目錄下。反例:易懂變量也要統(tǒng)一定義成應(yīng)用內(nèi)共享常量,兩位工程師在兩個(gè)類中分別定義了“YES”的變量:類A中:publicstaticfinalStringYES="yes";類B中:publicstaticfinalStringYES="y";A.YES.equals(B.YES),預(yù)期是true,但實(shí)際返回為false,導(dǎo)致線上問題。3)子工程內(nèi)部共享常量:即在當(dāng)前子工程的constant目錄下。4)包內(nèi)共享常量:即在當(dāng)前包下單獨(dú)的constant目錄下。5)類內(nèi)共享常量:直接在類內(nèi)部privatestaticfinal定義。5.【推薦】如果變量值僅在一個(gè)固定范圍內(nèi)變化用enum類型來定義。說明:如果存在名稱之外的延伸屬性應(yīng)使用enum類型,下面正例中的數(shù)字就是延伸信息,表示一年中的第幾個(gè)季節(jié)。正例:publicenumSeasonEnum{SPRING(1),SUMMER(2),AUTUMN(3),WINTER(4);privateintseq;SeasonEnum(intseq){this.seq=seq;}publicintgetSeq(){returnseq;}}(三) 代碼格式1.【強(qiáng)制】如果是大括號內(nèi)為空,則簡潔地寫成{}即可,大括號中間無需換行和空格;如果是非空代碼塊則:1)左大括號前不換行。2)左大括號后換行。3)右大括號前換行。4)右大括號后還有else等代碼則不換行;表示終止的右大括號后必須換行。2.【強(qiáng)制】左小括號和字符之間不出現(xiàn)空格;同樣,右小括號和字符之間也不出現(xiàn)空格;而左大括號前需要空格。詳見第5條下方正例提示。反例:if(空格a==b空格)3.【強(qiáng)制】if/for/while/switch/do等保留字與括號之間都必須加空格。4.【強(qiáng)制】任何二目、三目運(yùn)算符的左右兩邊都需要加一個(gè)空格。說明:運(yùn)算符包括賦值運(yùn)算符=、邏輯運(yùn)算符&&、加減乘除符號等。5.【強(qiáng)制】采用4個(gè)空格縮進(jìn),禁止使用tab字符。說明:如果使用tab縮進(jìn),必須設(shè)置1個(gè)tab為4個(gè)空格。IDEA設(shè)置tab為4個(gè)空格時(shí),請勿勾選Usetabcharacter;而在eclipse中,必須勾選insertspacesfortabs。正例:(涉及1-5點(diǎn))publicstaticvoidmain(String[]args){//縮進(jìn)4個(gè)空格Stringsay="hello";//運(yùn)算符的左右必須有一個(gè)空格intflag=0;//關(guān)鍵詞if與括號之間必須有一個(gè)空格,括號內(nèi)的f與左括號,0與右括號不需要空格if(flag==0){System.out.println(say);}//左大括號前加空格且不換行;左大括號后換行if(flag==1){//右大括號前換行,右大括號后有else,不用換行}else{System.out.println("ok");//在右大括號后直接結(jié)束,則必須換行}}6.【強(qiáng)制】注釋的雙斜線與注釋內(nèi)容之間有且僅有一個(gè)空格。正例://這是示例注釋,請注意在雙斜線之后有一個(gè)空格Stringparam=newString();7.【強(qiáng)制】在進(jìn)行類型強(qiáng)制轉(zhuǎn)換時(shí),右括號與強(qiáng)制轉(zhuǎn)換值之間不需要任何空格隔開。正例:longfirst=1000000000000L;intsecond=(int)first+2;8.【強(qiáng)制】單行字符數(shù)限制不超過120個(gè),超出需要換行,換行時(shí)遵循如下原則:1)第二行相對第一行縮進(jìn)4個(gè)空格,從第三行開始,不再繼續(xù)縮進(jìn),參考示例。2)運(yùn)算符與下文一起換行。3)方法調(diào)用的點(diǎn)符號與下文一起換行。4)方法調(diào)用中的多個(gè)參數(shù)需要換行時(shí),在逗號后進(jìn)行。5)在括號前不要換行,見反例。正例:StringBuildersb=newStringBuilder();//超過120個(gè)字符的情況下,換行縮進(jìn)4個(gè)空格,點(diǎn)號和方法名稱一起換行sb.append("Jack").append("Ma")append("alibaba")append("alibaba")append("alibaba");反例:StringBuildersb=newStringBuilder();//超過120個(gè)字符的情況下,不要在括號前換行sb.append("Jack").append("Ma")...append("alibaba");//參數(shù)很多的方法調(diào)用可能超過120個(gè)字符,不要在逗號前換行method(args1,args2,args3,...,argsX);9.【強(qiáng)制】方法參數(shù)在定義和傳入時(shí),多個(gè)參數(shù)逗號后邊必須加空格。正例:下例中實(shí)參的args1,后邊必須要有一個(gè)空格。method(args1,args2,args3);10.【強(qiáng)制】IDE的textfileencoding設(shè)置為UTF-8;IDE中文件的換行符使用Unix格式,不要使用Windows格式。11.【推薦】單個(gè)方法的總行數(shù)不超過80行。說明:除注釋之外的方法簽名、左右大括號、方法內(nèi)代碼、空行、回車及任何不可見字符的總行數(shù)不超過80行。正例:代碼邏輯分清紅花和綠葉,個(gè)性和共性,綠葉邏輯單獨(dú)出來成為額外方法,使主干代碼更加清晰;共性邏輯抽取成為共性方法,便于復(fù)用和維護(hù)。12.【推薦】沒有必要增加若干空格來使變量的賦值等號與上一行對應(yīng)位置的等號對齊。正例:intone=1;longtwo=2L;floatthree=3F;StringBuildersb=newStringBuilder();說明:增加sb這個(gè)變量,如果需要對齊,則給one、two、three都要增加幾個(gè)空格,在變量比較多的情況下,是非常累贅的事情。13.【推薦】不同邏輯、不同語義、不同業(yè)務(wù)的代碼之間插入一個(gè)空行分隔開來以提升可讀性。說明:任何情形,沒有必要插入多個(gè)空行進(jìn)行隔開。(四) OOP規(guī)約1.【強(qiáng)制】避免通過一個(gè)類的對象引用訪問此類的靜態(tài)變量或靜態(tài)方法,無謂增加編譯器解析成本,直接用類名來訪問即可。2.【強(qiáng)制】所有的覆寫方法,必須加@Override注解。說明:getObject()與get0bject()的問題。一個(gè)是字母的O,一個(gè)是數(shù)字的0,加@Override可以準(zhǔn)確判斷是否覆蓋成功。另外,如果在抽象類中對方法簽名進(jìn)行修改,其實(shí)現(xiàn)類會馬上編譯報(bào)錯(cuò)。3.【強(qiáng)制】相同參數(shù)類型,相同業(yè)務(wù)含義,才可以使用Java的可變參數(shù),避免使用Object。說明:可變參數(shù)必須放置在參數(shù)列表的最后。(提倡同學(xué)們盡量不用可變參數(shù)編程)正例:publicList<User>listUsers(Stringtype,Long...ids){...}4.【強(qiáng)制】外部正在調(diào)用或者二方庫依賴的接口,不允許修改方法簽名,避免對接口調(diào)用方產(chǎn)生影響。接口過時(shí)必須加@Deprecated注解,并清晰地說明采用的新接口或者新服務(wù)是什么。5.【強(qiáng)制】不能使用過時(shí)的類或方法。說明:.URLDecoder中的方法decode(StringencodeStr)這個(gè)方法已經(jīng)過時(shí),應(yīng)該使用雙參數(shù)decode(Stringsource,Stringencode)。接口提供方既然明確是過時(shí)接口,那么有義務(wù)同時(shí)提供新的接口;作為調(diào)用方來說,有義務(wù)去考證過時(shí)方法的新實(shí)現(xiàn)是什么。6.【強(qiáng)制】Object的equals方法容易拋空指針異常,應(yīng)使用常量或確定有值的對象來調(diào)用equals。說明:推薦使用java.util.Objects#equals(JDK7引入的工具類)。7.【強(qiáng)制】所有整型包裝類對象之間值的比較,全部使用equals方法比較。說明:對于Integervar=?在-128至范圍內(nèi)的賦值,Integer對象是在IntegerCache.cache產(chǎn)生,會復(fù)用已有對象,這個(gè)區(qū)間內(nèi)的Integer值可以直接使用==進(jìn)行判斷,但是這個(gè)區(qū)間之外的所有數(shù)據(jù),都會在堆上產(chǎn)生,并不會復(fù)用已有對象,這是一個(gè)大坑,推薦使用equals方法進(jìn)行判斷。8.【強(qiáng)制】浮點(diǎn)數(shù)之間的等值判斷,基本數(shù)據(jù)類型不能用==來比較,包裝數(shù)據(jù)類型不能用equals來判斷。說明:浮點(diǎn)數(shù)采用“尾數(shù)+階碼”的編碼方式,類似于科學(xué)計(jì)數(shù)法的“有效數(shù)字+指數(shù)”的表示方式。二進(jìn)制無法精確表示大部分的十進(jìn)制小數(shù),具體原理參考\h《碼出高效》。反例:floata=1.0f-0.9f;floatb=0.9f-0.8f;if(a==b){//預(yù)期進(jìn)入此代碼快,執(zhí)行其它業(yè)務(wù)邏輯//但事實(shí)上a==b的結(jié)果為false}Floatx=Float.valueOf(a);Floaty=Float.valueOf(b);if(x.equals(y)){//預(yù)期進(jìn)入此代碼快,執(zhí)行其它業(yè)務(wù)邏輯//但事實(shí)上equals的結(jié)果為false}正例:(1)指定一個(gè)誤差范圍,兩個(gè)浮點(diǎn)數(shù)的差值在此范圍之內(nèi),則認(rèn)為是相等的。floata=1.0f-0.9f;floatb=0.9f-0.8f;floatdiff=1e-6f;if(Math.abs(a-b)<diff){}(2)使用BigDecimal來定義值,再進(jìn)行浮點(diǎn)數(shù)的運(yùn)算操作。BigDecimala=newBigDecimal("1.0");BigDecimalb=newBigDecimal("0.9");BigDecimalc=newBigDecimal("0.8");BigDecimalx=a.subtract(b);BigDecimaly=b.subtract(c);if(x.equals(y)){}9.【強(qiáng)制】定義數(shù)據(jù)對象DO類時(shí),屬性類型要與數(shù)據(jù)庫字段類型相匹配。正例:數(shù)據(jù)庫字段的bigint必須與類屬性的Long類型相對應(yīng)。反例:某個(gè)案例的數(shù)據(jù)庫表id字段定義類型bigintunsigned,實(shí)際類對象屬性為Integer,隨著id越來越大,超過Integer的表示范圍而溢出成為負(fù)數(shù)。10.【強(qiáng)制】為了防止精度損失,禁止使用構(gòu)造方法BigDecimal(double)的方式把double值轉(zhuǎn)化為BigDecimal對象。說明:BigDecimal(double)存在精度損失風(fēng)險(xiǎn),在精確計(jì)算或值比較的場景中可能會導(dǎo)致業(yè)務(wù)邏輯異常。如:BigDecimalg=newBigDecimal(0.1f);實(shí)際的存儲值為:0.10000000149正例:優(yōu)先推薦入?yún)镾tring的構(gòu)造方法,或使用BigDecimal的valueOf方法,此方法內(nèi)部其實(shí)執(zhí)行了Double的toString,而Double的toString按double的實(shí)際能表達(dá)的精度對尾數(shù)進(jìn)行了截?cái)?。BigDecimalrecommend1=newBigDecimal("0.1");BigDecimalrecommend2=BigDecimal.valueOf(0.1);11.關(guān)于基本數(shù)據(jù)類型與包裝數(shù)據(jù)類型的使用標(biāo)準(zhǔn)如下:1)【強(qiáng)制】所有的POJO類屬性必須使用包裝數(shù)據(jù)類型。2)【強(qiáng)制】RPC方法的返回值和參數(shù)必須使用包裝數(shù)據(jù)類型。3)【推薦】所有的局部變量使用基本數(shù)據(jù)類型。說明:POJO類屬性沒有初值是提醒使用者在需要使用時(shí),必須自己顯式地進(jìn)行賦值,任何NPE問題,或者入庫檢查,都由使用者來保證。正例:數(shù)據(jù)庫的查詢結(jié)果可能是null,因?yàn)樽詣硬鹣?,用基本?shù)據(jù)類型接收有NPE風(fēng)險(xiǎn)。反例:比如顯示成交總額漲跌情況,即正負(fù)x%,x為基本數(shù)據(jù)類型,調(diào)用的RPC服務(wù),調(diào)用不成功時(shí),返回的是默認(rèn)值,頁面顯示為0%,這是不合理的,應(yīng)該顯示成中劃線。所以包裝數(shù)據(jù)類型的null值,能夠表示額外的信息,如:遠(yuǎn)程調(diào)用失敗,異常退出。12.【強(qiáng)制】定義DO/DTO/VO等POJO類時(shí),不要設(shè)定任何屬性默認(rèn)值。反例:POJO類的createTime默認(rèn)值為newDate(),但是這個(gè)屬性在數(shù)據(jù)提取時(shí)并沒有置入具體值,在更新其它字段時(shí)又附帶更新了此字段,導(dǎo)致創(chuàng)建時(shí)間被修改成當(dāng)前時(shí)間。13.【強(qiáng)制】序列化類新增屬性時(shí),請不要修改serialVersionUID字段,避免反序列失??;如果完全不兼容升級,避免反序列化混亂,那么請修改serialVersionUID值。說明:注意serialVersionUID不一致會拋出序列化運(yùn)行時(shí)異常。14.【強(qiáng)制】構(gòu)造方法里面禁止加入任何業(yè)務(wù)邏輯,如果有初始化邏輯,請放在init方法中。15.【強(qiáng)制】POJO類必須寫toString方法。使用IDE中的工具:source>generatetoString時(shí),如果繼承了另一個(gè)POJO類,注意在前面加一下super.toString。說明:在方法執(zhí)行拋出異常時(shí),可以直接調(diào)用POJO的toString()方法打印其屬性值,便于排查問題。16.【強(qiáng)制】禁止在POJO類中,同時(shí)存在對應(yīng)屬性xxx的isXxx()和getXxx()方法。說明:框架在調(diào)用屬性xxx的提取方法時(shí),并不能確定哪個(gè)方法一定是被優(yōu)先調(diào)用到。17.【推薦】使用索引訪問用String的split方法得到的數(shù)組時(shí),需做最后一個(gè)分隔符后有無內(nèi)容的檢查,否則會有拋IndexOutOfBoundsException的風(fēng)險(xiǎn)。說明:Stringstr="a,b,c,,";String[]ary=str.split(",");//預(yù)期大于3,結(jié)果是318.【推薦】當(dāng)一個(gè)類有多個(gè)構(gòu)造方法,或者多個(gè)同名方法,這些方法應(yīng)該按順序放置在一起,便于閱讀,此條規(guī)則優(yōu)先于下一條。19.【推薦】類內(nèi)方法定義的順序依次是:公有方法或保護(hù)方法>私有方法>getter/setter方法。說明:公有方法是類的調(diào)用者和維護(hù)者最關(guān)心的方法,首屏展示最好;保護(hù)方法雖然只是子類關(guān)心,也可能是“模板設(shè)計(jì)模式”下的核心方法;而私有方法外部一般不需要特別關(guān)心,是一個(gè)黑盒實(shí)現(xiàn);因?yàn)槌休d的信息價(jià)值較低,所有Service和DAO的getter/setter方法放在類體最后。20.【推薦】setter方法中,參數(shù)名稱與類成員變量名稱一致,this.成員名=參數(shù)名。在getter/setter方法中,不要增加業(yè)務(wù)邏輯,增加排查問題的難度。反例:publicIntegergetData(){if(condition){returnthis.data+100;}else{returnthis.data-100;}}21.【推薦】循環(huán)體內(nèi),字符串的連接方式,使用StringBuilder的append方法進(jìn)行擴(kuò)展。說明:下例中,反編譯出的字節(jié)碼文件顯示每次循環(huán)都會new出一個(gè)StringBuilder對象,然后進(jìn)行append操作,最后通過toString方法返回String對象,造成內(nèi)存資源浪費(fèi)。反例:Stringstr="start";for(inti=0;i<100;i++){str=str+"hello";}22.【推薦】final可以聲明類、成員變量、方法、以及本地變量,下列情況使用final關(guān)鍵字:1)不允許被繼承的類,如:String類。2)不允許修改引用的域?qū)ο蟆?)不允許被覆寫的方法,如:POJOsetter方法。4)不允許運(yùn)行過程中重新賦值的局部變量。5)避免上下文重復(fù)使用一個(gè)變量,使用final可以強(qiáng)制重新定義一個(gè)變量,方便更好地進(jìn)行重構(gòu)。23.【推薦】慎用Object的clone方法來拷貝對象。說明:對象clone方法默認(rèn)是淺拷貝,若想實(shí)現(xiàn)深拷貝需覆寫clone方法實(shí)現(xiàn)域?qū)ο蟮纳疃缺闅v式拷貝。24.【推薦】類成員與方法訪問控制從嚴(yán):1)如果不允許外部直接通過new來創(chuàng)建對象,那么構(gòu)造方法必須是private。2)工具類不允許有public或default構(gòu)造方法。3)類非static成員變量并且與子類共享,必須是protected。4)類非static成員變量并且僅在本類使用,必須是private。5)類static成員變量如果僅在本類使用,必須是private。6)若是static成員變量,考慮是否為final。7)類成員方法只供類內(nèi)部調(diào)用,必須是private。8)類成員方法只對繼承類公開,那么限制為protected。說明:任何類、方法、參數(shù)、變量,嚴(yán)控訪問范圍。過于寬泛的訪問范圍,不利于模塊解耦。思考:如果是一個(gè)private的方法,想刪除就刪除,可是一個(gè)public的service成員方法或成員變量,刪除一下,不得手心冒點(diǎn)汗嗎?變量像自己的小孩,盡量在自己的視線內(nèi),變量作用域太大,無限制的到處跑,那么你會擔(dān)心的。(五) 集合處理1.【強(qiáng)制】關(guān)于hashCode和equals的處理,遵循如下規(guī)則:1)只要覆寫equals,就必須覆寫hashCode。2)因?yàn)镾et存儲的是不重復(fù)的對象,依據(jù)hashCode和equals進(jìn)行判斷,所以存儲的對象必須覆寫這兩個(gè)方法。3)如果自定義對象作為Map的鍵,那么必須覆寫hashCode和equals。說明:String已覆寫hashCode和equals方法,所以我們可以愉快地使用String對象作為key來使用。2.【強(qiáng)制】ArrayList的subList結(jié)果不可強(qiáng)轉(zhuǎn)成ArrayList,否則會拋出ClassCastException異常,即java.util.RandomAccessSubListcannotbecasttojava.util.ArrayList。說明:subList返回的是ArrayList的內(nèi)部類SubList,并不是ArrayList而是ArrayList的一個(gè)視圖,對于SubList子列表的所有操作最終會反映到原列表上。3.【強(qiáng)制】使用Map的方法keySet()/values()/entrySet()返回集合對象時(shí),不可以對其進(jìn)行添加元素操作,否則會拋出UnsupportedOperationException異常。4.【強(qiáng)制】Collections類返回的對象,如:emptyList()/singletonList()等都是immutablelist,不可對其進(jìn)行添加或者刪除元素的操作。反例:如果查詢無結(jié)果,返回Collections.emptyList()空集合對象,調(diào)用方一旦進(jìn)行了添加元素的操作,就會觸發(fā)UnsupportedOperationException異常。5.【強(qiáng)制】在subList場景中,高度注意對原集合元素的增加或刪除,均會導(dǎo)致子列表的遍歷、增加、刪除產(chǎn)生ConcurrentModificationException異常。6.【強(qiáng)制】使用集合轉(zhuǎn)數(shù)組的方法,必須使用集合的toArray(T[]array),傳入的是類型完全一致、長度為0的空數(shù)組。反例:直接使用toArray無參方法存在問題,此方法返回值只能是Object[]類,若強(qiáng)轉(zhuǎn)其它類型數(shù)組將出現(xiàn)ClassCastException錯(cuò)誤。正例:List<String>list=newArrayList<>(2);list.add("guan");list.add("bao");String[]array=list.toArray(newString[0]);說明:使用toArray帶參方法,數(shù)組空間大小的length:1)等于0,動態(tài)創(chuàng)建與size相同的數(shù)組,性能最好。2)大于0但小于size,重新創(chuàng)建大小等于size的數(shù)組,增加GC負(fù)擔(dān)。3)等于size,在高并發(fā)情況下,數(shù)組創(chuàng)建完成之后,size正在變大的情況下,負(fù)面影響與上相同。4)大于size,空間浪費(fèi),且在size處插入null值,存在NPE隱患。7.【強(qiáng)制】在使用Collection接口任何實(shí)現(xiàn)類的addAll()方法時(shí),都要對輸入的集合參數(shù)進(jìn)行NPE判斷。說明:在ArrayList#addAll方法的第一行代碼即Object[]a=c.toArray();其中c為輸入集合參數(shù),如果為null,則直接拋出異常。8.【強(qiáng)制】使用工具類Arrays.asList()把數(shù)組轉(zhuǎn)換成集合時(shí),不能使用其修改集合相關(guān)的方法,它的add/remove/clear方法會拋出UnsupportedOperationException異常。說明:asList的返回對象是一個(gè)Arrays內(nèi)部類,并沒有實(shí)現(xiàn)集合的修改方法。Arrays.asList體現(xiàn)的是適配器模式,只是轉(zhuǎn)換接口,后臺的數(shù)據(jù)仍是數(shù)組。String[]str=newString[]{"yang","hao"};Listlist=Arrays.asList(str);第一種情況:list.add("yangguanbao");運(yùn)行時(shí)異常。第二種情況:str[0]="changed";也會隨之修改,反之亦然。9.【強(qiáng)制】泛型通配符<?extendsT>來接收返回的數(shù)據(jù),此寫法的泛型集合不能使用add方法,而<?superT>不能使用get方法,作為接口調(diào)用賦值時(shí)易出錯(cuò)。說明:擴(kuò)展說一下PECS(ProducerExtendsConsumerSuper)原則:第一、頻繁往外讀取內(nèi)容的,適合用<?extendsT>。第二、經(jīng)常往里插入的,適合用<?superT>10.【強(qiáng)制】在無泛型限制定義的集合賦值給泛型限制的集合時(shí),在使用集合元素時(shí),需要進(jìn)行instanceof判斷,避免拋出ClassCastException異常。說明:畢竟泛型是在JDK5后才出現(xiàn),考慮到向前兼容,編譯器是允許非泛型集合與泛型集合互相賦值。反例:List<String>generics=null;ListnotGenerics=newArrayList(10);notGenerics.add(newObject());notGenerics.add(newInteger(1));generics=notGenerics;//此處拋出ClassCastException異常Stringstring=generics.get(0);11.【強(qiáng)制】不要在foreach循環(huán)里進(jìn)行元素的remove/add操作。remove元素請使用Iterator方式,如果并發(fā)操作,需要對Iterator對象加鎖。正例:List<String>list=newArrayList<>();list.add("1");list.add("2");Iterator<String>iterator=list.iterator();while(iterator.hasNext()){Stringitem=iterator.next();if(刪除元素的條件){iterator.remove();}}反例:for(Stringitem:list){if("1".equals(item)){list.remove(item);}}說明:以上代碼的執(zhí)行結(jié)果肯定會出乎大家的意料,那么試一下把“1”換成“2”,會是同樣的結(jié)果嗎?12.【強(qiáng)制】在JDK7版本及以上,Comparator實(shí)現(xiàn)類要滿足如下三個(gè)條件,不然Arrays.sort,Collections.sort會拋IllegalArgumentException異常。說明:三個(gè)條件如下1)x,y的比較結(jié)果和y,x的比較結(jié)果相反。2)x>y,y>z,則x>z。3)x=y,則x,z比較結(jié)果和比較結(jié)果相同。反例:下例中沒有處理相等的情況,交換兩個(gè)對象判斷結(jié)果并不互反,不符合第一個(gè)條件,在實(shí)際使用中可能會出現(xiàn)異常。newComparator<Student>(){@Overridepublicintcompare(Studento1,Studento2){returno1.getId()>o2.getId()?1:-1;}};13.【推薦】集合泛型定義時(shí),在JDK7及以上,使用diamond語法或全省略。說明:菱形泛型,即diamond,直接使用<>來指代前邊已經(jīng)指定的類型。正例://diamond方式,即<>HashMap<String,String>userCache=newHashMap<>(16);//全省略方式ArrayList<User>users=newArrayList(10);14.【推薦】集合初始化時(shí),指定集合初始值大小。說明:HashMap使用HashMap(intinitialCapacity)初始化。正例:initialCapacity=(需要存儲的元素個(gè)數(shù)/負(fù)載因子)+1。注意負(fù)載因子(即loaderfactor)默認(rèn)為0.75,如果暫時(shí)無法確定初始值大小,請?jiān)O(shè)置為16(即默認(rèn)值)。反例:HashMap需要放置1024個(gè)元素,由于沒有設(shè)置容量初始大小,隨著元素不斷增加,容量7次被迫擴(kuò)大,resize需要重建hash表,嚴(yán)重影響性能。15.【推薦】使用entrySet遍歷類集合KV,而不是keySet方式進(jìn)行遍歷。說明:keySet其實(shí)是遍歷了2次,一次是轉(zhuǎn)為Iterator對象,另一次是從hashMap中取出key所對應(yīng)的value。而entrySet只是遍歷了一次就把key和value都放到了entry中,效率更高。如果是JDK8,使用Map.forEach方法。正例:values()返回的是V值集合,是一個(gè)list集合對象;keySet()返回的是K值集合,是一個(gè)集合對象;entrySet()返回的是K-V值組合集合。16.【推薦】高度注意Map類集合K/V能不能存儲null值的情況,如下表格:集合類KeyValueSuper說明Hashtable不允許為null不允許為nullDictionary線程安全ConcurrentHashMap不允許為null不允許為nullAbstractMap鎖分段技術(shù)(JDK8:CAS)TreeMap不允許為null允許為nullAbstractMap線程不安全HashMap允許為null允許為nullAbstractMap線程不安全反例:由于HashMap的干擾,很多人認(rèn)為ConcurrentHashMap是可以置入null值,而事實(shí)上,存儲null值時(shí)會拋出NPE異常。17.【參考】合理利用好集合的有序性(sort)和穩(wěn)定性(order),避免集合的無序性(unsort)和不穩(wěn)定性(unorder)帶來的負(fù)面影響。說明:有序性是指遍歷的結(jié)果是按某種比較規(guī)則依次排列的。穩(wěn)定性指集合每次遍歷的元素次序是一定的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是order/sort。18.【參考】利用Set元素唯一的特性,可以快速對一個(gè)集合進(jìn)行去重操作,避免使用List的contains方法進(jìn)行遍歷、對比、去重操作。(六) 并發(fā)處理1.【強(qiáng)制】獲取單例對象需要保證線程安全,其中的方法也要保證線程安全。說明:資源驅(qū)動類、工具類、單例工廠類都需要注意。2.【強(qiáng)制】創(chuàng)建線程或線程池時(shí)請指定有意義的線程名稱,方便出錯(cuò)時(shí)回溯。正例:自定義線程工廠,并且根據(jù)外部特征進(jìn)行分組,比如機(jī)房信息。publicclassUserThreadFactoryimplementsThreadFactory{privatefinalStringnamePrefix;privatefinalAtomicIntegernextId=newAtomicInteger(1);//定義線程組名稱,在jstack問題排查時(shí),非常有幫助UserThreadFactory(StringwhatFeaturOfGroup){namePrefix="FromUserThreadFactory's"+whatFeaturOfGroup+"-Worker-";}@OverridepublicThreadnewThread(Runnabletask){Stringname=namePrefix+nextId.getAndIncrement();Threadthread=newThread(null,task,name,0,false);System.out.println(thread.getName());returnthread;}}3.【強(qiáng)制】線程資源必須通過線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程。說明:線程池的好處是減少在創(chuàng)建和銷毀線程上所消耗的時(shí)間以及系統(tǒng)資源的開銷,解決資源不足的問題。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致消耗完內(nèi)存或者“過度切換”的問題。4.【強(qiáng)制】線程池不允許使用Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。說明:Executors返回的線程池對象的弊端如下:1)FixedThreadPool和SingleThreadPool:允許的請求隊(duì)列長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導(dǎo)致OOM。2)CachedThreadPool:允許的創(chuàng)建線程數(shù)量為Integer.MAX_VALUE,可能會創(chuàng)建大量的線程,從而導(dǎo)致OOM。5.【強(qiáng)制】SimpleDateFormat是線程不安全的類,一般不要定義為static變量,如果定義為static,必須加鎖,或者使用DateUtils工具類。正例:注意線程安全,使用DateUtils。亦推薦如下處理:privatestaticfinalThreadLocal<DateFormat>df=newThreadLocal<DateFormat>(){@OverrideprotectedDateFormatinitialValue(){returnnewSimpleDateFormat("yyyy-MM-dd");}};說明:如果是JDK8的應(yīng)用,可以使用InstantDate,LocalDateTimeCalendar,DateTimeFormatter代替SimpleDateFormat,官方給出的解釋:simplebeautifulstrongimmutablethread-safe。6.【強(qiáng)制】必須回收自定義的ThreadLocal變量,尤其在線程池場景下,線程經(jīng)常會被復(fù)用,如果不清理自定義的ThreadLocal變量,可能會影響后續(xù)業(yè)務(wù)邏輯和造成內(nèi)存泄露等問題。盡量在代理中使用try-finally塊進(jìn)行回收。正例:try{//...}finally{objectThreadLocal.remove();}7.【強(qiáng)制】高并發(fā)時(shí),同步調(diào)用應(yīng)該去考量鎖的性能損耗。能用無鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖;能鎖區(qū)塊,就不要鎖整個(gè)方法體;能用對象鎖,就不要用類鎖。說明:盡可能使加鎖的代碼塊工作量盡可能的小,避免在鎖代碼塊中調(diào)用RPC方法。8.【強(qiáng)制】對多個(gè)資源、數(shù)據(jù)庫表、對象同時(shí)加鎖時(shí),需要保持一致的加鎖順序,否則可能會造成死鎖。說明:線程一需要對表A、B、C依次全部加鎖后才可以進(jìn)行更新操作,那么線程二的加鎖順序也必須是A、B、C,否則可能出現(xiàn)死鎖。9.【強(qiáng)制】在使用阻塞等待獲取鎖的方式中,必須在try代碼塊之外,并且在加鎖方法與try代碼塊之間沒有任何可能拋出異常的方法調(diào)用,避免加鎖成功后,在finally中無法解鎖。說明一:如果在lock方法與try代碼塊之間的方法調(diào)用拋出異常,那么無法解鎖,造成其它線程無法成功獲取鎖。說明二:如果lock方法在try代碼塊之內(nèi),可能由于其它方法拋出異常,導(dǎo)致在finally代碼塊中,unlock對未加鎖的對象解鎖,它會調(diào)用AQS的tryRelease方法(取決于具體實(shí)現(xiàn)類),拋出IllegalMonitorStateException異常。說明三:在Lock對象的lock方法實(shí)現(xiàn)中可能拋出unchecked異常,產(chǎn)生的后果與說明二相同。正例:Locklock=newXxxLock();//...lock.lock();try{doOthers();}finally{lock.unlock();反例:}Locklock=newXxxLock();//...try{//如果此處拋出異常,則直接執(zhí)行finally代碼塊doSomething();//無論加鎖是否成功,finally代碼塊都會執(zhí)行l(wèi)ock.lock();}finally{lock.unlock();}10.【強(qiáng)制】在使用嘗試機(jī)制來獲取鎖的方式中,進(jìn)入業(yè)務(wù)代碼塊之前,必須先判斷當(dāng)前線程是否持有鎖。鎖的釋放規(guī)則與鎖的阻塞等待方式相同。說明:Lock對象的unlock方法在執(zhí)行時(shí),它會調(diào)用AQS的tryRelease方法(取決于具體實(shí)現(xiàn)類),如果當(dāng)前線程不持有鎖,則拋出IllegalMonitorStateException異常。正例:Locklock=newXxxLock();//...booleanisLocked=lock.tryLock();if(isLocked){try{doSomething();doOthers();}finally{lock.unlock();}}11.【強(qiáng)制】并發(fā)修改同一記錄時(shí),避免更新丟失,需要加鎖。要么在應(yīng)用層加鎖,要么在緩存加鎖,要么在數(shù)據(jù)庫層使用樂觀鎖,使用version作為更新依據(jù)。說明:如果每次訪問沖突概率小于20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數(shù)不得小于3次。12.【強(qiáng)制】多線程并行處理定時(shí)任務(wù)時(shí),Timer運(yùn)行多個(gè)TimeTask時(shí),只要其中之一沒有捕獲拋出的異常,其它任務(wù)便會自動終止運(yùn)行,如果在處理定時(shí)任務(wù)時(shí)使用ScheduledExecutorService則沒有這個(gè)問題。13.【推薦】資金相關(guān)的金融敏感信息,使用悲觀鎖策略。說明:樂觀鎖在獲得鎖的同時(shí)已經(jīng)完成了更新操作,校驗(yàn)邏輯容易出現(xiàn)漏洞,另外,樂觀鎖對沖突的解決策略有較復(fù)雜的要求,處理不當(dāng)容易造成系統(tǒng)壓力或數(shù)據(jù)異常,所以資金相關(guān)的金融敏感信息不建議使用樂觀鎖更新。14.【推薦】使用CountDownLatch進(jìn)行異步轉(zhuǎn)同步操作,每個(gè)線程退出前必須調(diào)用countDown方法,線程執(zhí)行代碼注意catch異常,確保countDown方法被執(zhí)行到,避免主線程無法執(zhí)行至await方法,直到超時(shí)才返回結(jié)果。說明:注意,子線程拋出異常堆棧,不能在主線程try-catch到。15.【推薦】避免Random實(shí)例被多線程使用,雖然共享該實(shí)例是線程安全的,但會因競爭同一seed導(dǎo)致的性能下降。說明:Random實(shí)例包括java.util.Random的實(shí)例或者M(jìn)ath.random()的方式。正例:在JDK7之后,可以直接使用APIThreadLocalRandom,而在JDK7之前,需要編碼保證每個(gè)線程持有一個(gè)實(shí)例。16.【推薦】在并發(fā)場景下,通過雙重檢查鎖(double-checkedlocking)實(shí)現(xiàn)延遲初始化的優(yōu)化問題隱患(可參考The"Double-CheckedLockingBroken"Declaration),推薦解決方案中較為簡單一種(適用于JDK5及以上版本),將目標(biāo)屬性聲明為volatile型。反例:publicclassLazyInitDemo{privateHelperhelper=null;publicHelpergetHelper(){if(helper==null)synchronized(this){if(helper==null)helper=newHelper();}returnhelper;}//othermethodsandfields...}17.【參考】volatile解決多線程內(nèi)存不可見問題。對于一寫多讀,是可以解決變量同步問題,但是如果多寫,同樣無法解決線程安全問題。說明:如果是count++操作,使用如下類實(shí)現(xiàn):AtomicIntegercount=newAtomicInteger();count.addAndGet(1);如果是JDK8,推薦使用LongAdder對象,比AtomicLong性能更好(減少樂觀鎖的重試次數(shù))。18.【參考】HashMap在容量不夠進(jìn)行resize時(shí)由于高并發(fā)可能出現(xiàn)死鏈,導(dǎo)致CPU飆升,在開發(fā)過程中可以使用其它數(shù)據(jù)結(jié)構(gòu)或加鎖來規(guī)避此風(fēng)險(xiǎn)。19.【參考】ThreadLocal對象使用static修飾,ThreadLocal無法解決共享對象的更新問題。說明:這個(gè)變量是針對一個(gè)線程內(nèi)所有操作共享的,所以設(shè)置為靜態(tài)變量,所有此類實(shí)例共享此靜態(tài)變量,也就是說在類第一次被使用時(shí)裝載,只分配一塊存儲空間,所有此類的對象(只要是這個(gè)線程內(nèi)定義的)都可以操控這個(gè)變量。(七) 控制語句1.【強(qiáng)制】在一個(gè)switch塊內(nèi),每個(gè)case要么通過continue/break/return等來終止,要么注釋說明程序?qū)⒗^續(xù)執(zhí)行到哪一個(gè)case為止;在一個(gè)switch塊內(nèi),都必須包含一個(gè)default語句并且放在最后,即使它什么代碼也沒有。說明:注意break是退出switch語句塊,而return是退出方法體。2.【強(qiáng)制】當(dāng)switch括號內(nèi)的變量類型為String并且此變量為外部參數(shù)時(shí),必須先進(jìn)行null判斷。反例:猜猜下面的代碼輸出是什么?publicclassSwitchString{publicstaticvoidmain(String[]args){method(null);}publicstaticvoidmethod(Stringparam){switch(param){//肯定不是進(jìn)入這里case"sth":System.out.println("it'ssth");break;//也不是進(jìn)入這里case"null":System.out.println("it'snull");break;//也不是進(jìn)入這里default:System.out.println("default");}}3.【強(qiáng)制】在if/else/for/while/do語句中必須使用大括號。說明:即使只有一行代碼,避免采用單行的編碼方式:if(condition)statements;4.【強(qiáng)制】在高并發(fā)場景中,避免使用”等于”判斷作為中斷或退出的條件。說明:如果并發(fā)控制沒有處理好,容易產(chǎn)生等值判斷被“擊穿”的情況,使用大于或小于的區(qū)間判斷條件來代替。反例:判斷剩余獎(jiǎng)品數(shù)量等于0時(shí),終止發(fā)放獎(jiǎng)品,但因?yàn)椴l(fā)處理錯(cuò)誤導(dǎo)致獎(jiǎng)品數(shù)量瞬間變成了負(fù)數(shù),這樣的話,活動無法終止。5.【推薦】表達(dá)異常的分支時(shí),少用if-else方式,這種方式可以改寫成:if(condition){...returnobj;}//接著寫else的業(yè)務(wù)邏輯代碼;說明:如果非使用if()...elseif()...else...方式表達(dá)邏輯,避免后續(xù)代碼維護(hù)困難,【強(qiáng)制】請勿超過3層。正例:超過3層的if-else的邏輯判斷代碼可以使用衛(wèi)語句、策略模式、狀態(tài)模式等來實(shí)現(xiàn),其中衛(wèi)語句即代碼邏輯先考慮失敗、異常、中斷、退出等直接返回的情況,以方法多個(gè)出口的方式,解決代碼中判斷分支嵌套的問題,這是逆向思維的體現(xiàn)。示例如下:publicvoidfindBoyfriend(Manman){if(man.isUgly()){System.out.println("本姑娘是外貌協(xié)會的資深會員");return;}if(man.isPoor()){System.out.println("貧賤夫妻百事哀");return;}if(man.isBadTemper()){System.out.println("銀河有多遠(yuǎn),你就給我滾多遠(yuǎn)");return;}System.out.println("可以先交往一段時(shí)間看看");}6.【推薦】除常用方法(如getXxx/isXxx)等外,不要在條件判斷中執(zhí)行其它復(fù)雜的語句,將復(fù)雜邏輯判斷的結(jié)果賦值給一個(gè)有意義的布爾變量名,以提高可讀性。說明:很多if語句內(nèi)的邏輯表達(dá)式相當(dāng)復(fù)雜,與、或、取反混合運(yùn)算,甚至各種方法縱深調(diào)用,理解成本非常高。如果賦值一個(gè)非常好理解的布爾變量名字,則是件令人爽心悅目的事情。正例://偽代碼如下finalbooleanexisted=(file.open(fileName,"w")!=null)&&(...)||(...);if(existed){...}反例:publicfinalvoidacquire(longarg){if(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg)){selfInterrupt();}}7.【推薦】不要在其它表達(dá)式(尤其是條件表達(dá)式)中,插入賦值語句。說明:賦值點(diǎn)類似于人體的穴位,對于代碼的理解至關(guān)重要,所以賦值語句需要清晰地單獨(dú)成為一行。反例:publicLockgetLock(booleanfair){//算術(shù)表達(dá)式中出現(xiàn)賦值操作,容易忽略count值已經(jīng)被改變threshold=(count=Integer.MAX_VALUE)-1;//條件表達(dá)式中出現(xiàn)賦值操作,容易誤認(rèn)為是sync==fairreturn(sync=fair)?newFairSync():newNonfairSync();}8.【推薦】循環(huán)體中的語句要考量性能,以下操作盡量移至循環(huán)體外處理,如定義對象、變量、獲取數(shù)據(jù)庫連接,進(jìn)行不必要的try-catch操作(這個(gè)try-catch是否可以移至循環(huán)體外)。9.【推薦】避免采用取反邏輯運(yùn)算符。說明:取反邏輯不利于快速理解,并且取反邏輯寫法必然存在對應(yīng)的正向邏輯寫法。正例:使用if(x<628)來表達(dá)x小于628。反例:使用if(!(x>=628))來表達(dá)x小于628。10.【推薦】接口入?yún)⒈Wo(hù),這種場景常見的是用作批量操作的接口。11.【參考】下列情形,需要進(jìn)行參數(shù)校驗(yàn):1)調(diào)用頻次低的方法。2)執(zhí)行時(shí)間開銷很大的方法。此情形中,參數(shù)校驗(yàn)時(shí)間幾乎可以忽略不計(jì),但如果因?yàn)閰?shù)錯(cuò)誤導(dǎo)致中間執(zhí)行回退,或者錯(cuò)誤,那得不償失。3)需要極高穩(wěn)定性和可用性的方法。4)對外提供的開放接口,不管是RPC/API/HTTP接口。5)敏感權(quán)限入口。12.【參考】下列情形,不需要進(jìn)行參數(shù)校驗(yàn):1)極有可能被循環(huán)調(diào)用的方法。但在方法說明里必須注明外部參數(shù)檢查要求。2)底層調(diào)用頻度比較高的方法。畢竟是像純凈水過濾的最后一道,參數(shù)錯(cuò)誤不太可能到底層才會暴露問題。一般DAOService層都在同一個(gè)應(yīng)用中,部署在同一臺服務(wù)器中,所以DAO的參數(shù)校驗(yàn),可以省略。3)被聲明成private只會被自己代碼所調(diào)用的方法,如果能夠確定調(diào)用方法的代碼傳入?yún)?shù)已經(jīng)做過檢查或者肯定不會有問題,此時(shí)可以不校驗(yàn)參數(shù)。(八) 注釋規(guī)約1.【強(qiáng)制】類、類屬性、類方法的注釋必須使用Javadoc規(guī)范,使用/**內(nèi)容*/格式,不得使用//xxx方式。說明:在IDE編輯窗口中,Javadoc方式會提示相關(guān)注釋,生成Javadoc可以正確輸出相應(yīng)注釋;在IDE中,工程調(diào)用方法時(shí),不進(jìn)入方法即可懸浮提示方法、參數(shù)、返回值的意義,提高閱讀效率。2.【強(qiáng)制】所有的抽象方法(包括接口中的方法)必須要用Javadoc注釋、除了返回值、參數(shù)、異常說明外,還必須指出該方法做什么事情,實(shí)現(xiàn)什么功能。說明:對子類的實(shí)現(xiàn)要求,或者調(diào)用注意事項(xiàng),請一并說明。3.【強(qiáng)制】所有的類都必須添加創(chuàng)建者和創(chuàng)建日期。4.【強(qiáng)制】方法內(nèi)部單行注釋,在被注釋語句上方另起一行,使用//注釋。方法內(nèi)部多行注釋使用/**/注釋,注意與代碼對齊。5.【強(qiáng)制】所有的枚舉類型字段必須要有注釋,說明每個(gè)數(shù)據(jù)項(xiàng)的用途。6.【推薦】與其“半吊子”英文來注釋,不如用中文注釋把問題說清楚。專有名詞與關(guān)鍵字保持英文原文即可。反例:“TCP連接超時(shí)”解釋成“傳輸控制協(xié)議連接超時(shí)”,理解反而費(fèi)腦筋。7.【推薦】代碼修改的同時(shí),注釋也要進(jìn)行相應(yīng)的修改,尤其是參數(shù)、返回值、異常、核心邏輯等的修改。說明:代碼與注釋更新不同步,就像路網(wǎng)與導(dǎo)航軟件更新不同步一樣,如果導(dǎo)航軟件嚴(yán)重滯后,就失去了導(dǎo)航的意義。8.【參考】謹(jǐn)慎注釋掉代碼。在上方詳細(xì)說明,而不是簡單地注釋掉。如果無用,則刪除。說明:代碼被注釋掉有兩種可能性:1)后續(xù)會恢復(fù)此段代碼邏輯。2)永久不用。前者如果沒有備注信息,難以知曉注釋動機(jī)。后者建議直接刪掉(代碼倉庫已然保存了歷史代碼)。9.【參考】對于注釋的要求:第一、能夠準(zhǔn)確反映設(shè)計(jì)思想和代碼邏輯;第二、能夠描述業(yè)務(wù)含義,使別的程序員能夠迅速了解到代碼背后的信息。完全沒有注釋的大段代碼對于閱讀者形同天書,注釋是給自己看的,即使隔很長時(shí)間,也能清晰理解當(dāng)時(shí)的思路;注釋也是給繼任者看的,使其能夠快速接替自己的工作。10.【參考】好的命名、代碼結(jié)構(gòu)是自解釋的,注釋力求精簡準(zhǔn)確、表達(dá)到位。避免出現(xiàn)注釋的一個(gè)極端:過多過濫的注釋,代碼的邏輯一旦修改,修改注釋是相當(dāng)大的負(fù)擔(dān)。反例://putelephantintofridgeput(elephant,fridge);方法名put,加上兩個(gè)有意義的變量名elephant和fridge,已經(jīng)說明了這是在干什么,語義清晰的代碼不需要額外的注釋。11.【參考】特殊注釋標(biāo)記,請注明標(biāo)記人與標(biāo)記時(shí)間。注意及時(shí)處理這些標(biāo)記,通過標(biāo)記掃描,經(jīng)常清理此類標(biāo)記。線上故障有時(shí)候就是來源于這些標(biāo)記處的代碼。1)待辦事宜(TODO):(標(biāo)記人,標(biāo)記時(shí)間,[預(yù)計(jì)處理時(shí)間])表示需要實(shí)現(xiàn),但目前還未實(shí)現(xiàn)的功能。這實(shí)際上是一個(gè)Javadoc的標(biāo)簽,目前的Javadoc還沒有實(shí)現(xiàn),但已經(jīng)被廣泛使用。只能應(yīng)用于類,接口和方法(因?yàn)樗且粋€(gè)Javadoc標(biāo)簽)。2)錯(cuò)誤,不能工作(FIXME):(標(biāo)記人,標(biāo)記時(shí)間,[預(yù)計(jì)處理時(shí)間])在注釋中用FIXME標(biāo)記某代碼是錯(cuò)誤的,而且不能工作,需要及時(shí)糾正的情況。(九) 其它1.【強(qiáng)制】在使用正則表達(dá)式時(shí),利用好其預(yù)編譯功能,可以有效加快正則匹配速度。說明:不要在方法體內(nèi)定義:Patternpattern=Ppile(“規(guī)則”);2.【強(qiáng)制】velocity調(diào)用POJO類的屬性時(shí),直接使用屬性名取值即可,模板引擎會自動按規(guī)范調(diào)用POJO的getXxx(),如果是boolean基本數(shù)據(jù)類型變量(boolean命名不需要加is前綴),會自動調(diào)用isXxx()方法。說明:注意如果是Boolean包裝類對象,優(yōu)先調(diào)用getXxx()的方法。3.【強(qiáng)制】后臺輸送給頁面的變量必須加$!{var}——中間的感嘆號。說明:如果var等于null或者不存在,那么${var}會直接顯示在頁面上。4.【強(qiáng)制】注意Math.random()這個(gè)方法返回是double類型,注意取值的范圍0≤x<1(能夠取到零值,注意除零異常),如果想獲取整數(shù)類型的隨機(jī)數(shù),不要將x放大10的若干倍然后取整,直接使用Random對象的nextInt或者nextLong方法。5.【強(qiáng)制】獲取當(dāng)前毫秒數(shù)System.currentTimeMillis();而不是newDate().getTime();說明:如果想獲取更加精確的納秒級時(shí)間值,使用System.nanoTime()的方式。在JDK8中,針對統(tǒng)計(jì)時(shí)間等場景,推薦使用Instant類。6.【強(qiáng)制】日期格式化時(shí),傳入pattern中表示年份統(tǒng)一使用小寫的y。說明:日期格式化時(shí),yyyy表示當(dāng)天所在的年,而大寫的YYYY代表是weekinwhichyear(JDK7之后引入的概念),意思是當(dāng)天所在的周屬于的年份,一周從周日開始,周六結(jié)束,只要本周跨年,返回的YYYY就是下一年。另外需要注意:? 表示月份是大寫的M? 表示分鐘則是小寫的m? 24小時(shí)制的是大寫的H? 12小時(shí)制的則是小寫的h正例:表示日期和時(shí)間的格式如下所示:newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");7.【推薦】不要在視圖模板中加入任何復(fù)雜的邏輯。說明:根據(jù)MVC理論,視圖的職責(zé)是展示,不要搶模型和控制器的活。8.【推薦】任何數(shù)據(jù)結(jié)構(gòu)的構(gòu)造或初始化,都應(yīng)指定大小,避免數(shù)據(jù)結(jié)構(gòu)無限增長吃光內(nèi)存。9.【推薦】及時(shí)清理不再使用的代碼段或配置信息。說明:對于垃圾代碼或過時(shí)配置,堅(jiān)決清理干凈,避免程序過度臃腫,代碼冗余。正例:對于暫時(shí)被注釋掉,后續(xù)可能恢復(fù)使用的代碼片斷,在注釋代碼上方,統(tǒng)一規(guī)定使用三個(gè)斜杠(///)來說明注釋掉代碼的理由。二、異常日志1.【強(qiáng)制】Java類庫中定義的可以通過預(yù)檢查方式規(guī)避的RuntimeException異常不應(yīng)該通過catch的方式來處理,比如:NullPointerException,IndexOutOfBoundsException等等。說明:無法通過預(yù)檢查的異常除外,比如,在解析字符串形式的數(shù)字時(shí),可能存在數(shù)字格式錯(cuò)誤,不得不通過catchNumberFormatException來實(shí)現(xiàn)。正例:if(obj!=null){...}反例:try{obj.method();}catch(NullPointerExceptione){…}2.【強(qiáng)制】異常不要用來做流程控制,條件控制。說明:異常設(shè)計(jì)的初衷是解決程序運(yùn)行中的各種意外情況,且異常的處理效率比條件判斷方式要低很多。3.【強(qiáng)制】catch時(shí)請分清穩(wěn)定代碼和非穩(wěn)定代碼,穩(wěn)定代碼指的是無論如何不會出錯(cuò)的代碼。對于非穩(wěn)定代碼的catch盡可能進(jìn)行區(qū)分異常類型,再做對應(yīng)的異常處理。說明:對大段代碼進(jìn)行try-catch,使程序無法根據(jù)不同的異常做出正確的應(yīng)激反應(yīng),也不利于定位問題,這是一種不負(fù)責(zé)任的表現(xiàn)。正例:用戶注冊的場景中,如果用戶輸入非法字符,或用戶名稱已存在,或用戶輸入密碼過于簡單,在程序上作出分門別類的判斷,并提示給用戶。4.【強(qiáng)制】捕獲異常是為了處理它,不要捕獲了卻什么都不處理而拋棄之,如果不想處理它,請將該異常拋給它的調(diào)用者。最外層的業(yè)務(wù)使用者,必須處理異常,將其轉(zhuǎn)化為用戶可以理解的內(nèi)容。5.【強(qiáng)制】有try塊放到了事務(wù)代碼中,catch異常后,如果需要回滾事務(wù),一定要注意手動回滾事務(wù)。6.【強(qiáng)制】finally塊必須對資源對象、流對象進(jìn)行關(guān)閉,有異常也要做try-catch。說明:如果JDK7及以上,可以使用try-with-resources方式?!緩?qiáng)制】不要在finally塊中使用return。說明:try塊中的return語句執(zhí)行成功后,并不馬上返回,而是繼續(xù)執(zhí)行finally塊中的語句,如果此處存在return語句,則在此直接返回,無情丟棄掉try塊中的返回點(diǎn)。反例:privateintx=0;publicintcheckReturn(){try{//x等于1,此處不返回return++x;}finally{//返回的結(jié)果是2return++x;}}8.【強(qiáng)制】捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類。說明:如果預(yù)期對方拋的是繡球,實(shí)際接到的是鉛球,就會產(chǎn)生意外情況。9.【強(qiáng)制】在調(diào)用RPC、二方包、或動態(tài)生成類的相關(guān)方法時(shí),捕捉異常必須使用Throwable類來進(jìn)行攔截。說明:通過反射機(jī)制來調(diào)用方法,如果找不到方法,拋出NoSuchMethodException。什么情況會拋出NoSuchMethodError呢?二方包在類沖突時(shí),仲裁機(jī)制可能導(dǎo)致引入非預(yù)期的版本使類的方法簽名不匹配,或者在字節(jié)碼修改框架(比如:ASM)動態(tài)創(chuàng)建或修改類時(shí),修改了相應(yīng)的方法簽名。這些情況,即使代碼編譯期是正確的,但在代碼運(yùn)行期時(shí),會拋出NoSuchMethodError。10.【推薦】方法的返回值可以為null,不強(qiáng)制返回空集合,或者空對象等,必須添加注釋充分說明什么情況下會返回null值。說明:本手冊明確防止NPE是調(diào)用者的責(zé)任。即使被調(diào)用方法返回空集合或者空對象,對調(diào)用者來說,也并非高枕無憂,必須考慮到遠(yuǎn)程調(diào)用失敗、序列化失敗、運(yùn)行時(shí)異常等場景返回null的情況。11.【推薦】防止NPE,是程序員的基本修養(yǎng),注意NPE產(chǎn)生的場景:1)返回類型為基本數(shù)據(jù)類型,return包裝數(shù)據(jù)類型的對象時(shí),自動拆箱有可能產(chǎn)生NPE。反例:publicintf(){returnInteger對象},如果為null,自動解箱拋NPE。2)數(shù)據(jù)庫的查詢結(jié)果可能為null。3)集合里的元素即使isNotEmpty,取出的數(shù)據(jù)元素也可能為null。4)遠(yuǎn)程調(diào)用返回對象時(shí),一律要求進(jìn)行空指針判斷,防止NPE。5)對于Session中獲取的數(shù)據(jù),建議進(jìn)行NPE檢查,避免空指針。6)級聯(lián)調(diào)用obj.getA().getB().getC();一連串調(diào)用,易產(chǎn)生NPE。

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論