2019Java 華山版開發(fā)手冊 1.5.0_第1頁
2019Java 華山版開發(fā)手冊 1.5.0_第2頁
2019Java 華山版開發(fā)手冊 1.5.0_第3頁
2019Java 華山版開發(fā)手冊 1.5.0_第4頁
2019Java 華山版開發(fā)手冊 1.5.0_第5頁
已閱讀5頁,還剩42頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

Java華山版開發(fā)手冊1.5.0前 言《JavaJava約、MySQL七個維度,再根據(jù)內(nèi)容特征,細(xì)分成若干二級子目錄。另外,依據(jù)約束力強(qiáng)弱及故障敏感性,規(guī)約依次分為強(qiáng)制、推薦、參考三大類。在延伸信息中,“說明”對規(guī)約做了適當(dāng)擴(kuò)展和解釋;“正例”提倡什么樣的編碼和實(shí)現(xiàn)方式;“反例”說明需要提防的雷區(qū),以及真實(shí)的錯誤案例。手冊的愿景是碼出高效,碼出質(zhì)量。現(xiàn)代軟件架構(gòu)的復(fù)雜性需要協(xié)同開發(fā)完成,如何高效地協(xié)同呢?無規(guī)矩不成方圓,無規(guī)范難以協(xié)同,比如,制訂交通法規(guī)表面上是要限制行車權(quán),實(shí)際上是保障公眾的人身安全,試想如果沒有限速,沒有紅綠燈,誰還敢上路行駛?對軟件來說,適當(dāng)?shù)囊?guī)范和標(biāo)準(zhǔn)絕不是消滅代碼內(nèi)容的創(chuàng)造性、優(yōu)雅性,而是限制過度個性化,以一種普遍認(rèn)可的統(tǒng)一方式一起做事,提升協(xié)作效率,降低溝通成本。代碼的字里行間流淌的是軟件系統(tǒng)的血液,質(zhì)量的提升是盡可能少踩坑,杜絕踩重復(fù)的坑,切實(shí)提升系統(tǒng)穩(wěn)定性,碼出質(zhì)量。目錄前言一、編程規(guī)約 1(一) 命名風(fēng)格 1一、編程規(guī)約 1(二) 常量定義 4(三) 代碼格式 5(四) OOP規(guī)約 7(五) 集合處理 11(六) 并發(fā)處理 14(七) 控制語句 18(八) 注釋規(guī)約 21(九) 其它 22二、異常日志 24(一) 異常處理 24(二) 日志規(guī)約 26三、單元測試 28四、安全規(guī)約 30五、MySQL數(shù)據(jù)庫 31(一) 建表規(guī)約 31(二) 索引規(guī)約 32(三) SQL語句 34(四) ORM映射 35六、工程結(jié)構(gòu) 37(一) 應(yīng)用分層 37(二) 二方庫依賴 38附1:版本歷史 43附2:專有名詞解釋附1:版本歷史 43附2:專有名詞解釋 44七、設(shè)計規(guī)約 41Java開發(fā)手冊Java開發(fā)手冊PAGEPAGE12/44版本號制定團(tuán)隊(duì)更新日期備注1.5.0阿里巴巴與Java社區(qū)開發(fā)者2019.06.19華山版,新增21條,修改描述112處一、編程規(guī)約(一) 命名風(fēng)格【強(qiáng)制】代碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結(jié)束。反例:_name/name/$name/name_/name$/name【強(qiáng)制】代碼中的命名嚴(yán)禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。說明:正確的英文拼寫和語法可以讓閱讀者易于理解,避免歧義。注意,純拼音命名方式更要避免采用。正例:renminbi/alibaba/taobao/youku/hangzhou等國際通用的名稱,可視同英文。反例:DaZhePromotion[打折]/getPingfenByName()[評分]/int某變量=3【強(qiáng)制】UpperCamelCaseDO/BO/DTO/VO/AO/PO/UID等。正例:JavaServerlessPlatform/UserDO/XmlService/TcpUdpDeal/TaPromotion反例:javaserverlessplatform/UserDo/XMLService/TCPUDPDeal/TAPromotion【強(qiáng)制】lowerCamelCase從駝峰形式。正例:localValue/getHttpMessage()/inputUserId【強(qiáng)制】長。正例:MAX_STOCK_COUNT/CACHE_EXPIRED_TIME反例:MAX_COUNT/EXPIRED_TIME【強(qiáng)制】AbstractBaseExceptionTest【強(qiáng)制】類型與中括號緊挨相連來表示數(shù)組。正例:定義整形數(shù)組int[]arrayDemo;反例:在main參數(shù)中,使用Stringargs[]來定義。【強(qiáng)制】POJOis說明:MySQLis_xxx的命名方式,所以,需要在<resultMap>設(shè)置從is_xxx到xxx的映射關(guān)系。反例:定義為基本數(shù)據(jù)類型BooleanisDeleted的屬性,它的方法也是isDeleted(),RPC框架在反向解析的時候,“誤以為”對應(yīng)的屬性名稱是deleted,導(dǎo)致屬性獲取不到,進(jìn)而拋出異常?!緩?qiáng)制】用單數(shù)形式,但是類名如果有復(fù)數(shù)含義,類名可以使用復(fù)數(shù)形式。正例:應(yīng)用工具類包名為com.alibaba.ai.util、類名為MessageUtils(此規(guī)則參考spring的框架結(jié)構(gòu))【強(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;}【強(qiáng)制】杜絕完全不規(guī)范的縮寫,避免望文不知義。反例:AbstractClass“縮寫”命名成AbsClass;condition“縮寫”命名成condi,此類隨意縮寫嚴(yán)重降低了代碼的可閱讀性?!就扑]】組合來表達(dá)其意。正例:在JDK中,表達(dá)原子更新的類名為:AtomicReferenceFieldUpdater。反例:inta的隨意命名方式?!就扑]】在常量與變量的命名時,表示類型的名詞放在詞尾,以提升辨識度。正例:startTime/workQueue/nameList/TERMINATED_THREAD_COUNT反例:startedAt/QueueOfWork/listName/COUNT_TERMINATED_THREAD【推薦】如果模塊、接口、類、方法使用了設(shè)計模式,在命名時需體現(xiàn)出具體模式。說明:將設(shè)計模式體現(xiàn)在名字中,有利于閱讀者快速理解架構(gòu)設(shè)計理念。正例:publicclassOrderFactory;publicclassLoginProxy;publicclassResourceObserver;【推薦】接口類中的方法和屬性不要加任何修飾符號(public也不要加),Javadoc正例:接口方法簽名voidcommit();接口基礎(chǔ)常量StringCOMPANY="alibaba";反例:接口方法定義publicabstractvoidf();說明:JDK8中接口允許有默認(rèn)實(shí)現(xiàn),那么這個default方法,是對所有實(shí)現(xiàn)類都有價值的默認(rèn)實(shí)現(xiàn)。接口和實(shí)現(xiàn)類的命名有兩套規(guī)則:【強(qiáng)制】ServiceDAOSOAImpl的后綴與接口區(qū)別。正例:CacheServiceImpl實(shí)現(xiàn)CacheService接口?!就扑]】如果是形容能力的接口名稱,取對應(yīng)的形容詞為接口名(通常是–able)正例:AbstractTranslator實(shí)現(xiàn)Translatable【參考】Enum說明:枚舉其實(shí)就是特殊的類,域成員均為常量,且構(gòu)造方法被默認(rèn)強(qiáng)制是私有。正例:枚舉名字為ProcessStatusEnum的成員名稱:SUCCESS/UNKNOWN_REASON?!緟⒖肌扛鲗用?guī)約:Service/DAOgetlistlistObjects。count插入的方法用save/insert做前綴。5)刪除的方法用remove/delete6)修改的方法用update領(lǐng)域模型命名規(guī)約數(shù)據(jù)對象:xxxDO,xxx數(shù)據(jù)傳輸對象:xxxDTO,xxx展示對象:xxxVO,xxxPOJODO/DTO/BO/VOxxxPOJO。(二) 常量定義【強(qiáng)制】不允許任何魔法值(即未經(jīng)預(yù)先定義的常量)直接出現(xiàn)在代碼中。反例:Stringkey="Id#taobao_"+tradeId;cache.put(key,value);//緩存get時,由于在代碼復(fù)制時,漏掉下劃線,導(dǎo)致緩存擊穿而出現(xiàn)問題【強(qiáng)制】long或者LongLl,小寫容易跟數(shù)1說明:Longa=2l;寫的是數(shù)字的21,還是Long型的2?!就扑]】不要使用一個常量類維護(hù)所有常量,要按常量功能進(jìn)行歸類,分開維護(hù)。說明:正例:CacheConstsConfigConsts【推薦】包內(nèi)共享常量、類內(nèi)共享常量。client.jarconstantconstant反例:易懂變量也要統(tǒng)一定義成應(yīng)用內(nèi)共享常量,兩位工程師在兩個類中分別定義了“YES”的變量:類A中:publicstaticfinalStringYES="yes";類B中:publicstaticfinalStringYES="y";A.YES.equals(B.YEStruefalse,導(dǎo)致線上問題。constantconstantprivatestaticfinal【推薦】enum說明:如果存在名稱之外的延伸屬性應(yīng)使用enum類型,下面正例中的數(shù)字就是延伸信息,表示一年中的第幾個季節(jié)。正例:publicenumSeasonEnum{SPRING(1),SUMMER(2),AUTUMN(3),WINTER(4);privateintseq;SeasonEnum(intseq){this.seq=seq;}publicintgetSeq(){returnseq;}}(三) 代碼格式【強(qiáng)制】空代碼塊則:左大括號前不換行。左大括號后換行。右大括號前換行。else【強(qiáng)制】左小括號和字符之間不出現(xiàn)空格;5反例:if(空格a==b空格)【強(qiáng)制】if/for/while/switch/do【強(qiáng)制】任何二目、三目運(yùn)算符的左右兩邊都需要加一個空格。說明:運(yùn)算符包括賦值運(yùn)算符=、邏輯運(yùn)算符&&、加減乘除符號等?!緩?qiáng)制】4tab說明:如果使用tab縮進(jìn),必須設(shè)置1個tab為4個空格。IDEA設(shè)置tab為4個空格時,請勿勾選Usetabcharacter;而在eclipse中,必須勾選insertspacesfortabs。正例:(涉及1-5點(diǎn))publicstaticvoidmain(String[]args){//縮進(jìn)4個空格Stringsay="hello";//運(yùn)算符的左右必須有一個空格intflag=0;//關(guān)鍵詞if與括號之間必須有一個空格,括號內(nèi)的f與左括號,0與右括號不需要空格if(flag==0){System.out.println(say);}//左大括號前加空格且不換行;左大括號后換行if(flag==1){System.out.println("world");//右大括號前換行,右大括號后有else,不用換行}else{System.out.println("ok");//在右大括號后直接結(jié)束,則必須換行}}【強(qiáng)制】注釋的雙斜線與注釋內(nèi)容之間有且僅有一個空格。正例://這是示例注釋,請注意在雙斜線之后有一個空格Stringparam=newString();【強(qiáng)制】在進(jìn)行類型強(qiáng)制轉(zhuǎn)換時,右括號與強(qiáng)制轉(zhuǎn)換值之間不需要任何空格隔開。正例:longfirst=1000000000000L;intsecond=(int)first+2;【強(qiáng)制】1204運(yùn)算符與下文一起換行。方法調(diào)用的點(diǎn)符號與下文一起換行。方法調(diào)用中的多個參數(shù)需要換行時,在逗號后進(jìn)行。在括號前不要換行,見反例正例:StringBuildersb=newStringBuilder();//超過120個字符的情況下,換行縮進(jìn)4個空格,點(diǎn)號和方法名稱一起換行sb.append("Jack").append("Ma")append("alibaba")append("alibaba")append("alibaba");反例:StringBuildersb=newStringBuilder();//超過120個字符的情況下,不要在括號前換行sb.append("Jack").append("Ma")...append("alibaba");//參數(shù)很多的方法調(diào)用可能超過120個字符,不要在逗號前換行method(args1,args2,args3,...,argsX);【強(qiáng)制】方法參數(shù)在定義和傳入時,多個參數(shù)逗號后邊必須加空格。正例:下例中實(shí)參的args1,后邊必須要有一個空格。method(args1,args2,args3);【強(qiáng)制】IDEtextfileencodingUTF-8;IDEUnixWindows【推薦】80說明:除注釋之外的方法簽名、左右大括號、方法內(nèi)代碼、空行、回車及任何不可見字符的總行數(shù)不超過80行。正例:代碼邏輯分清紅花和綠葉,個性和共性,綠葉邏輯單獨(dú)出來成為額外方法,使主干代碼更加清晰;共性邏輯抽取成為共性方法,便于復(fù)用和維護(hù)?!就扑]】沒有必要增加若干空格來使變量的賦值等號與上一行對應(yīng)位置的等號對齊。正例:intone=1;longtwo=2L;floatthree=StringBuildersb=newStringBuilder();說明:增加sb這個變量,如果需要對齊,則給one、two、three都要增加幾個空格,在變量比較多的情況下,是非常累贅的事情?!就扑]】不同邏輯、不同語義、不同業(yè)務(wù)的代碼之間插入一個空行分隔開來以提升可讀性。說明:任何情形,沒有必要插入多個空行進(jìn)行隔開。(四) OOP規(guī)約【強(qiáng)制】成本,直接用類名來訪問即可。【強(qiáng)制】所有的覆寫方法,必須加@Override說明:getObject()與get0bject()的問題。一個是字母的O,一個是數(shù)字的0,加@Override可以準(zhǔn)確判斷是否覆蓋成功。另外,如果在抽象類中對方法簽名進(jìn)行修改,其實(shí)現(xiàn)類會馬上編譯報錯?!緩?qiáng)制】JavaObject。說明:可變參數(shù)必須放置在參數(shù)列表的最后。(提倡同學(xué)們盡量不用可變參數(shù)編程)正例:publicList<User>listUsers(Stringtype,Long...ids){...}【強(qiáng)制】生影響。接口過時必須加@Deprecated【強(qiáng)制】不能使用過時的類或方法。說明:.URLDecoder中的方法decode(StringencodeStr)這個方法已經(jīng)過時,應(yīng)該使用雙參數(shù)decode(Stringsource,Stringencode)。接口提供方既然明確是過時接口,那么有義務(wù)同時提供新的接口;作為調(diào)用方來說,有義務(wù)去考證過時方法的新實(shí)現(xiàn)是什么。【強(qiáng)制】Objectequalsequals。正例:"test".equals(object);反例:object.equals("test");說明:推薦使用java.util.Objects#equals(JDK7引入的工具類)。【強(qiáng)制】所有整型包裝類對象之間值的比較,equals說明:Integervar=?在-128Integer對象是在IntegerCache.cacheIntegerequals【強(qiáng)制】浮點(diǎn)數(shù)之間的等值判斷,基本數(shù)據(jù)類型不能用==來比較,包裝數(shù)據(jù)類型不能用equals來判斷。說明:浮點(diǎn)數(shù)采用“尾數(shù)+階碼”的編碼方式,類似于科學(xué)計數(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ù)邏輯//a==b的結(jié)果為false}Floatx=Float.valueOf(a);Floaty=Float.valueOf(b);if(x.equals(y)){//預(yù)期進(jìn)入此代碼快,執(zhí)行其它業(yè)務(wù)邏輯//equals的結(jié)果為false}正例:指定一個誤差范圍,兩個浮點(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){System.out.println("true");}BigDecimalBigDecimala=newBigDecimal("1.0");BigDecimalb=newBigDecimal("0.9");BigDecimalc=newBigDecimal("0.8");BigDecimalx=a.subtract(b);BigDecimaly=b.subtract(c);if(x.equals(y)){System.out.println("true");}【強(qiáng)制】DO正例:數(shù)據(jù)庫字段的bigint必須與類屬性的Long類型相對應(yīng)。反例:某個案例的數(shù)據(jù)庫表id字段定義類型bigintunsigned,實(shí)際類對象屬性為Integer,隨著id越來越大,超過Integer的表示范圍而溢出成為負(fù)數(shù)?!緩?qiáng)制】BigDecimal(double)double轉(zhuǎn)BigDecimal對象。說明:BigDecimal(double)存在精度損失風(fēng)險,在精確計算或值比較的場景中可能會導(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)行了截斷。BigDecimalrecommend1=newBigDecimal("0.1");BigDecimalrecommend2=BigDecimal.valueOf(0.1);關(guān)于基本數(shù)據(jù)類型與包裝數(shù)據(jù)類型的使用標(biāo)準(zhǔn)如下:【強(qiáng)制】POJO【強(qiáng)制】RPC【推薦】所有的局部變量使用基本數(shù)據(jù)類型。說明:POJO類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進(jìn)行賦值,任何NPE問題,或者入庫檢查,都由使用者來保證。正例:數(shù)據(jù)庫的查詢結(jié)果可能是null,因?yàn)樽詣硬鹣?,用基本?shù)據(jù)類型接收有NPE風(fēng)險。反例:x%,xRPC0null夠表示額外的信息,如:遠(yuǎn)程調(diào)用失敗,異常退出。【強(qiáng)制】DO/DTO/VOPOJO默認(rèn)值。反例:POJOcreateTimenewDate(),但是這個屬性在數(shù)據(jù)提取時并沒有置入具體值,在更新其它字段時又附帶更新了此字段,導(dǎo)致創(chuàng)建時間被修改成當(dāng)前時間?!緩?qiáng)制】serialVersionUIDserialVersionUID說明:注意serialVersionUID不一致會拋出序列化運(yùn)行時異常。【強(qiáng)制】init【強(qiáng)制】POJOtoStringIDEsourcegeneratetoString時,如果繼承了另一個POJO類,注意在前面加一下super.toString。說明:在方法執(zhí)行拋出異常時,可以直接調(diào)用POJO的toString()方法打印其屬性值,便于排查問題?!緩?qiáng)制】POJOxxxisXxx()getXxx()方法。說明:框架在調(diào)用屬性xxx的提取方法時,并不能確定哪個方法一定是被優(yōu)先調(diào)用到?!就扑]】StringsplitIndexOutOfBoundsException說明:Stringstr="a,b,c,,";String[]ary=str.split(",");//預(yù)期大于3,結(jié)果是3System.out.println(ary.length);【推薦】便于閱讀,此條規(guī)則優(yōu)先于下一條?!就扑]】getter/setter方法。說明:公有方法是類的調(diào)用者和維護(hù)者最關(guān)心的方法,首屏展示最好;保護(hù)方法雖然只是子類關(guān)心,也可能是“模板設(shè)計模式”下的核心方法;而私有方法外部一般不需要特別關(guān)心,是一個黑盒實(shí)現(xiàn);因?yàn)槌休d的信息價值較低,所有Service和DAO的getter/setter方法放在類體最后?!就扑]】setterthis.成員名=參數(shù)名。在getter/setter方法中,不要增加業(yè)務(wù)邏輯,增加排查問題的難度。反例:publicIntegergetData(){if(condition){returnthis.data+100;}else{returnthis.data-100;}}【推薦】StringBuilderappendnewStringBuildertoStringString反例:Stringstr="start";for(inti=0;i<100;i++){str=str+"hello";}【推薦】finalfinal關(guān)鍵字:不允許被繼承的類,如:String不允許修改引用的域?qū)ο?。不允許被覆寫的方法,如:POJOsetter不允許運(yùn)行過程中重新賦值的局部變量。final【推薦】Objectclone方法來拷貝對象。說明:對象clone方法默認(rèn)是淺拷貝,若想實(shí)現(xiàn)深拷貝需覆寫clone方法實(shí)現(xiàn)域?qū)ο蟮纳疃缺闅v式拷貝?!就扑]】類成員與方法訪問控制從嚴(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成員變量,考慮是否為tected。說明:任何類、方法、參數(shù)、變量,嚴(yán)控訪問范圍。過于寬泛的訪問范圍,不利于模塊解耦。思考:如果privatepublicservice會擔(dān)心的。(五) 集合處理【強(qiáng)制】hashCodeequalsequalshashCode。SethashCodeequalsMap的鍵,那么必須覆寫hashCodeequals。說明:String已覆寫hashCode和equals方法,所以我們可以愉快地使用String對象作為key來使用?!緩?qiáng)制】ArrayListsubListArrayList,否則會拋出ClassCastException異java.util.RandomAccessSubListcannotbecasttojava.util.ArrayList。說明:subList返回的是ArrayList的內(nèi)部類SubList,并不是ArrayList而是ArrayList的一個視圖,對于SubList子列表的所有操作最終會反映到原列表上?!緩?qiáng)制】MapkeySet()/values()/entrySet()UnsupportedOperationException異常。【強(qiáng)制】Collections類返回的對象,如:emptyList()/singletonList()immutablelist,不可對其進(jìn)行添加或者刪除元素的操作。反例:如果查詢無結(jié)果,返回Collections.emptyList()空集合對象,調(diào)用方一旦進(jìn)行了添加元素的操作,就會觸發(fā)UnsupportedOperationException異常?!緩?qiáng)制】subList高度注意ConcurrentModificationException異常?!緩?qiáng)制】toArray(Tarray0反例:直接使用toArray無參方法存在問題,此方法返回值只能是Object[]類,若強(qiáng)轉(zhuǎn)其它類型數(shù)組將出現(xiàn)ClassCastException錯誤。正例:List<String>list=newArrayList<>(2);list.add("guan");list.add("bao");String[]array=list.toArray(newString[0]);說明:toArray帶參方法,數(shù)組空間大小的length:1)0size0但小于sizesizeGC負(fù)擔(dān)。size,在高并發(fā)情況下,數(shù)組創(chuàng)建完成之后,size正在變大的情況下,負(fù)面影響與上相同。sizesizenullNPE隱患。【強(qiáng)制】CollectionaddAll()方法時,都要對輸入的集合參數(shù)進(jìn)行NPE說明:在ArrayList#addAll方法的第一行代碼即Object[]a=c.toArray();其中c為輸入集合參數(shù),如果為null,則直接拋出異常?!緩?qiáng)制】Arrays.asList(add/remove/clearUnsupportedOperationException說明:asList的返回對象是一個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)行時異常。第二種情況:str[0]="changed";也會隨之修改,反之亦然。【強(qiáng)制】泛型通配符extendsT>add方法,而superT>get方法,作為接口調(diào)用賦值時易出錯。說明:擴(kuò)展說一下PECS(ProducerExtendsConsumerSuper)原則:第一、頻繁往外讀取內(nèi)容的,適合用<?extendsT>。第二、經(jīng)常往里插入的,適合用<?superT>【強(qiáng)制】在無泛型限制定義的集合賦值給泛型限制的集合時,在使用集合元素時,需要進(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);【強(qiáng)制】foreachremove/addremoveIterator方式,如果并發(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é)果嗎?【強(qiáng)制】JDK7ComparatorArrays.sort,Collections.sortIllegalArgumentException說明:三個條件如下x,yy,xx>y,y>zx>z。x=yx,z反例:下例中沒有處理相等的情況,交換兩個對象判斷結(jié)果并不互反,不符合第一個條件,在實(shí)際使用中可能會出現(xiàn)異常。newComparator<Student>(){@Overridepublicintcompare(Studento1,Studento2){returno1.getId()>o2.getId()?1:-1;}};【推薦】JDK7diamond說明:菱形泛型,即diamond,直接使用<>來指代前邊已經(jīng)指定的類型。正例://diamond方式,即<>HashMap<String,String>userCache=newHashMap<>(16);//全省略方式ArrayList<User>users=newArrayList(10);【推薦】集合初始化時,指定集合初始值大小。說明:HashMapHashMap(intinitialCapacity)初始化。正例:initialCapacity=(需要存儲的元素個數(shù)/負(fù)載因子)+1。注意負(fù)載因子(即loaderfactor)默認(rèn)為0.75,如果暫時無法確定初始值大小,請?jiān)O(shè)置為16(即默認(rèn)值)。反例:HashMap需要放置1024個元素,由于沒有設(shè)置容量初始大小,隨著元素不斷增加,容量7次被迫擴(kuò)大,resize需要重建hash表,嚴(yán)重影響性能。【推薦】entrySetKVkeySet方式進(jìn)行遍歷。說明:keySet2IteratorhashMapkeyvalueentrySetkeyvalueentryJDK8,Map.forEach正例:values()返回的是V值集合,是一個list集合對象;keySet()返回的是K值集合,是一個Set集合對象;entrySet()返回的是K-V值組合集合?!就扑]】MapK/Vnull集合類KeyValueSuper說明Hashtable不允許為null不允許為nullDictionary線程安全ConcurrentHashMap不允許為null不允許為nullAbstractMap鎖分段技術(shù)(JDK8:CAS)TreeMap不允許為null允許為nullAbstractMap線程不安全HashMap允許為null允許為nullAbstractMap線程不安全反例:由于HashMap的干擾,很多人認(rèn)為ConcurrentHashMap是可以置入null值,而事實(shí)上,存儲null值時會拋出NPE異常?!緟⒖肌亢侠砝煤眉系挠行蛐?sort)和穩(wěn)定性(order),避免集合的無序性(unsort)和不穩(wěn)定性(unorder)帶來的負(fù)面影響。說明:有序性是指遍歷的結(jié)果是按某種比較規(guī)則依次排列的。穩(wěn)定性指集合每次遍歷的元素次序是一定的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是order/sort?!緟⒖肌縎etListcontains方法進(jìn)行遍歷、對比、去重操作。(六) 并發(fā)處理【強(qiáng)制】獲取單例對象需要保證線程安全,其中的方法也要保證線程安全。說明:資源驅(qū)動類、工具類、單例工廠類都需要注意。【強(qiáng)制】創(chuàng)建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。正例:自定義線程工廠,并且根據(jù)外部特征進(jìn)行分組,比如機(jī)房信息。publicclassUserThreadFactoryimplementsThreadFactory{privatefinalStringnamePrefix;privatefinalAtomicIntegernextId=newAtomicInteger(1);//定義線程組名稱,在jstack問題排查時,非常有幫助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;}}【強(qiáng)制】線程資源必須通過線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程。說明:線程池的好處是減少在創(chuàng)建和銷毀線程上所消耗的時間以及系統(tǒng)資源的開銷,解決資源不足的問【強(qiáng)制】ExecutorsThreadPoolExecutor說明:Executors返回的線程池對象的弊端如下:1)FixedThreadPool和SingleThreadPool:允許的請求隊(duì)列長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導(dǎo)致OOM。2)CachedThreadPool:允許的創(chuàng)建線程數(shù)量為Integer.MAX_VALUE,可能會創(chuàng)建大量的線程,從而導(dǎo)致OOM。【強(qiáng)制】SimpleDateFormatstatic變量,如果定義為static,必須加鎖,或者使用DateUtils工具類。正例:注意線程安全,使用DateUtils。亦推薦如下處理:privatestaticfinalThreadLocal<DateFormat>df=newThreadLocal<DateFormat>(){@OverrideprotectedDateFormatinitialValue(){returnnewSimpleDateFormat("yyyy-MM-dd");}};說明:如果是JDK8的應(yīng)用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatterSimpleDateFormatsimplebeautifulstrongimmutablethread-safe?!緩?qiáng)制】ThreadLocaltry-finally正例:objectThreadLocal.set(userInfo);try{//...}finally{objectThreadLocal.remove();}【強(qiáng)制】高并發(fā)時,同步調(diào)用應(yīng)該去考量鎖的性能損耗。能用無鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖能鎖區(qū)塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。說明:盡可能使加鎖的代碼塊工作量盡可能的小,避免在鎖代碼塊中調(diào)用RPC方法?!緩?qiáng)制】造成死鎖。說明:線程一需要對表A、B、C依次全部加鎖后才可以進(jìn)行更新操作,那么線程二的加鎖順序也必須是A、B、C,否則可能出現(xiàn)死鎖?!緩?qiáng)制】trytryfinally中無法解鎖。說明一:如果在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{doSomething();doOthers();}finally{lock.unlock();反例:

}Locklock=newXxxLock();//...try{//如果此處拋出異常,則直接執(zhí)行finally代碼塊doSomething();//無論加鎖是否成功,finally代碼塊都會執(zhí)行l(wèi)ock.lock();doOthers();}finally{lock.unlock();}【強(qiáng)制】否持有鎖。鎖的釋放規(guī)則與鎖的阻塞等待方式相同。說明:Lock對象的unlock方法在執(zhí)行時,它會調(diào)用AQS的tryRelease方法(取決于具體實(shí)現(xiàn)類),如果當(dāng)前線程不持有鎖,則拋出IllegalMonitorStateException異常。正例:Locklock=newXxxLock();//...booleanisLocked=lock.tryLock();if(isLocked){try{doSomething();doOthers();}finally{lock.unlock();}}【強(qiáng)制】version作為更新依據(jù)。說明:如果每次訪問沖突概率小于20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數(shù)不得小于3次。【強(qiáng)制】多線程并行處理定時任務(wù)時,TimerTimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務(wù)便會自動終止運(yùn)行,如果在處理定時任務(wù)時使用ScheduledExecutorService則沒有這個問題?!就扑]】資金相關(guān)的金融敏感信息,使用悲觀鎖策略。說明:樂觀鎖在獲得鎖的同時已經(jīng)完成了更新操作,校驗(yàn)邏輯容易出現(xiàn)漏洞,另外,樂觀鎖對沖突的解決策略有較復(fù)雜的要求,處理不當(dāng)容易造成系統(tǒng)壓力或數(shù)據(jù)異常,所以資金相關(guān)的金融敏感信息不建議使用樂觀鎖更新?!就扑]】CountDownLatchcountDowncatchcountDown方法被執(zhí)行到,避免主線程無法執(zhí)行await方法,直到超時才返回結(jié)果。說明:注意,子線程拋出異常堆棧,不能在主線程try-catch到?!就扑]】Random實(shí)例被多線程使用,雖然共享該實(shí)例是線程安全的,但會因競爭同一seed導(dǎo)致的性能下降。說明:Random實(shí)例包括java.util.Random的實(shí)例或者M(jìn)ath.random()的方式。正例:在JDK7之后,可以直接使用APIThreadLocalRandom,而在JDK7之前,需要編碼保證每個線程持有一個實(shí)例。【推薦】在并發(fā)場景下,通過雙重檢查鎖(double-checkedlocking)實(shí)現(xiàn)延遲初始化的優(yōu)化問題隱患(可參考The"Double-CheckedLockingBrokenDeclaration),推薦解決方案中較為簡單一種(JDK5),將目標(biāo)屬性聲明為volatile型。反例:publicclassLazyInitDemo{privateHelperhelper=null;publicHelpergetHelper(){if(helper==null)synchronized(this){if(helper==null)helper=newHelper();}returnhelper;}//othermethodsandfields...}Java開發(fā)手冊Java開發(fā)手冊PAGEPAGE24/44【參考】volatile是如果多寫,同樣無法解決線程安全問題。說明:如果是count++操作,使用如下類實(shí)現(xiàn):AtomicIntegercount=newAtomicInteger();count.addAndGet(1);如果是JDK8,推薦使用LongAdder對象,比AtomicLong性能更好(減少樂觀鎖的重試次數(shù))?!緟⒖肌縃ashMapresizeCPU【參考】ThreadLocalstaticThreadLocal(七) 控制語句【強(qiáng)制】switchcasecontinue/break/returncaseswitch說明:注意break是退出switch語句塊,而return是退出方法體?!緩?qiáng)制】switchStringnull判斷。反例:猜猜下面的代碼輸出是什么?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");}}}【強(qiáng)制】if/else/for/while/do說明:即使只有一行代碼,避免采用單行的編碼方式:if(condition)statements;【強(qiáng)制】在高并發(fā)場景中,避免使用”等于”判斷作為中斷或退出的條件。說明:如果并發(fā)控制沒有處理好,容易產(chǎn)生等值判斷被“擊穿”的情況,使用大于或小于的區(qū)間判斷條件來代替。反例:判斷剩余獎品數(shù)量等于0時,終止發(fā)放獎品,但因?yàn)椴l(fā)處理錯誤導(dǎo)致獎品數(shù)量瞬間變成了負(fù)數(shù),這樣的話,活動無法終止?!就扑]】if-else,這種方式可以改寫成:if(condition){...returnobj;}//接著寫else的業(yè)務(wù)邏輯代碼;說明:if()...elseif()...else...方式表達(dá)邏輯,避免后續(xù)代碼維護(hù)困難,【強(qiáng)制】33層的if-else的邏輯判斷代碼可以使用衛(wèi)語句、策略模式、狀態(tài)模式等來實(shí)現(xiàn),其中衛(wèi)語句即代碼邏輯先考慮失敗、異常、中斷、退出等直接返回的情況,以方法多個出口的方式,解決代碼中判斷分支嵌套的問題,這是逆向思維的體現(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("可以先交往一段時間看看");}【推薦】除常用方法(getXxx/isXxx)雜邏輯判斷的結(jié)果賦值給一個有意義的布爾變量名,以提高可讀性。說明:很多if語句內(nèi)的邏輯表達(dá)式相當(dāng)復(fù)雜,與、或、取反混合運(yùn)算,甚至各種方法縱深調(diào)用,理解成本非常高。如果賦值一個非常好理解的布爾變量名字,則是件令人爽心悅目的事情。正例://偽代碼如下finalbooleanexisted=(file.open(fileName,"w")!=null)&&(...)||(...);if(existed){...}反例:publicfinalvoidacquire(longarg){if(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg)){selfInterrupt();}}【推薦】不要在其它表達(dá)式(尤其是條件表達(dá)式)中,插入賦值語句。說明:賦值點(diǎn)類似于人體的穴位,對于代碼的理解至關(guān)重要,所以賦值語句需要清晰地單獨(dú)成為一行。反例:publicLockgetLock(booleanfair){//算術(shù)表達(dá)式中出現(xiàn)賦值操作,容易忽略count值已經(jīng)被改變threshold=(count=Integer.MAX_VALUE)-1;//sync==fairreturn(sync=fair)?newFairSync():newNonfairSync();}【推薦】try-catch(try-catch是否可以移至循環(huán)體外)?!就扑]】避免采用取反邏輯運(yùn)算符。說明:取反邏輯不利于快速理解,并且取反邏輯寫法必然存在對應(yīng)的正向邏輯寫法。正例:使用if(x<628)來表達(dá)x小于628。反例:使用if(!(x>=628))來表達(dá)x小于628?!就扑]】接口入?yún)⒈Wo(hù),這種場景常見的是用作批量操作的接口?!緟⒖肌肯铝星樾危枰M(jìn)行參數(shù)校驗(yàn):調(diào)用頻次低的方法。中間執(zhí)行回退,或者錯誤,那得不償失。需要極高穩(wěn)定性和可用性的方法。RPC/API/HTTP敏感權(quán)限入口?!緟⒖肌肯铝星樾?,不需要進(jìn)行參數(shù)校驗(yàn):極有可能被循環(huán)調(diào)用的方法。但在方法說明里必須注明外部參數(shù)檢查要求。底層調(diào)用頻度比較高的方法。畢竟是像純凈水過濾的最后一道,參數(shù)錯誤不太可能到底層才會暴露DAOServiceDAOprivate查或者肯定不會有問題,此時可以不校驗(yàn)參數(shù)。(八) 注釋規(guī)約【強(qiáng)制】Javadoc/**內(nèi)容*/格式,不得使用//xxx方式。說明:在IDE編輯窗口中,Javadoc方式會提示相關(guān)注釋,生成Javadoc可以正確輸出相應(yīng)注釋;在IDE中,工程調(diào)用方法時,不進(jìn)入方法即可懸浮提示方法、參數(shù)、返回值的意義,提高閱讀效率?!緩?qiáng)制】所有的抽象方法(包括接口中的方法)Javadoc異常說明外,還必須指出該方法做什么事情,實(shí)現(xiàn)什么功能。說明:對子類的實(shí)現(xiàn)要求,或者調(diào)用注意事項(xiàng),請一并說明?!緩?qiáng)制】所有的類都必須添加創(chuàng)建者和創(chuàng)建日期?!緩?qiáng)制】方法內(nèi)部單行注釋,在被注釋語句上方另起一行,使用//使用/**/注釋,注意與代碼對齊?!緩?qiáng)制】所有的枚舉類型字段必須要有注釋,說明每個數(shù)據(jù)項(xiàng)的用途?!就扑]】持英文原文即可。反例:“TCP連接超時”解釋成“傳輸控制協(xié)議連接超時”,理解反而費(fèi)腦筋。【推薦】輯等的修改。說明:代碼與注釋更新不同步,就像路網(wǎng)與導(dǎo)航軟件更新不同步一樣,如果導(dǎo)航軟件嚴(yán)重滯后,就失去了導(dǎo)航的意義?!緟⒖肌恐?jǐn)慎注釋掉代碼。在上方詳細(xì)說明,而不是簡單地注釋掉。如果無用,則刪除。說明:代碼被注釋掉有兩種可能性:1)后續(xù)會恢復(fù)此段代碼邏輯。2)息,難以知曉注釋動機(jī)。后者建議直接刪掉(代碼倉庫已然保存了歷史代碼)?!緟⒖肌繉τ谧⑨尩囊螅旱谝?、能夠準(zhǔn)確反映設(shè)計思想和代碼邏輯;第二、能夠描述業(yè)務(wù)形同天書,注釋是給自己看的,即使隔很長時間,也能清晰理解當(dāng)時的思路;注釋也是給繼任者看的,使其能夠快速接替自己的工作?!緟⒖肌恳粋€極端:過多過濫的注釋,代碼的邏輯一旦修改,修改注釋是相當(dāng)大的負(fù)擔(dān)。反例://putelephantintofridgeput(elephant,fridge);方法名put,加上兩個有意義的變量名elephant和fridge,已經(jīng)說明了這是在干什么,語義清晰的代碼不需要額外的注釋?!緟⒖肌棵?,經(jīng)常清理此類標(biāo)記。線上故障有時候就是來源于這些標(biāo)記處的代碼。待辦事宜(TODO):(標(biāo)記人,標(biāo)記時間,[預(yù)計處理時間])表示需要實(shí)現(xiàn),但目前還未實(shí)現(xiàn)的功能。這實(shí)際上是一個Javadoc的標(biāo)簽,目前的Javadoc還沒有實(shí)現(xiàn),但已經(jīng)被廣泛使用。只能應(yīng)用于類,接口和方法(因?yàn)樗且粋€Javadoc標(biāo)簽)。錯誤,不能工作(FIXME):(標(biāo)記人,標(biāo)記時間,[預(yù)計處理時間])在注釋中用FIXME標(biāo)記某代碼是錯誤的,而且不能工作,需要及時糾正的情況。(九) 其它【強(qiáng)制】在使用正則表達(dá)式時,利用好其預(yù)編譯功能,可以有效加快正則匹配速度。說明:不要在方法體內(nèi)定義:Patternpattern=Ppile(“規(guī)則”);【強(qiáng)制】velocityPOJOPOJOgetXxx()boolean(booleanis前綴),會自動調(diào)用isXxx()方法。說明:注意如果是Boolean包裝類對象,優(yōu)先調(diào)用getXxx()的方法?!緩?qiáng)制】后臺輸送給頁面的變量必須加$!{var}——中間的感嘆號。說明:如果var等于null或者不存在,那么${var}會直接顯示在頁面上?!緩?qiáng)制】注意Math.random()double類型,注意取值的范圍0≤x<1(能夠取到零值,注意除零異常)x10RandomnextIntnextLong方法。【強(qiáng)制】System.currentTimeMillis();newDate().getTime();說明:System.nanoTimeJDK8Instant【強(qiáng)制】pattern。說明:日期格式化時,yyyyYYYYweekinwhichyear(JDK7之后引入的概念),YYYY就是下一年。另外需要注意:Mm24H12h正例:表示日期和時間的格式如下所示:newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");【推薦】不要在視圖模板中加入任何復(fù)雜的邏輯。說明:根據(jù)MVC理論,視圖的職責(zé)是展示,不要搶模型和控制器的活。【推薦】任何數(shù)據(jù)結(jié)構(gòu)的構(gòu)造或初始化,都應(yīng)指定大小,避免數(shù)據(jù)結(jié)構(gòu)無限增長吃光內(nèi)存?!就扑]】及時清理不再使用的代碼段或配置信息。說明:對于垃圾代碼或過時配置,堅(jiān)決清理干凈,避免程序過度臃腫,代碼冗余。正例:對于暫時被注釋掉,后續(xù)可能恢復(fù)使用的代碼片斷,在注釋代碼上方,統(tǒng)一規(guī)定使用三個斜杠(///)來說明注釋掉代碼的理由。二、異常日志(一) 異常處理【強(qiáng)制】JavaRuntimeException異常不應(yīng)該通catch的方式來處理,比如:NullPointerException,IndexOutOfBoundsException等等。說明:無法通過預(yù)檢查的異常除外,比如,在解析字符串形式的數(shù)字時,可能存在數(shù)字格式錯誤,不得不通過catchNumberFormatException來實(shí)現(xiàn)。正例:if(obj!=null){...}反例:try{obj.method();}catch(NullPointerExceptione){…}【強(qiáng)制】異常不要用來做流程控制,條件控制。說明:異常設(shè)計的初衷是解決程序運(yùn)行中的各種意外情況,且異常的處理效率比條件判斷方式要低很多。【強(qiáng)制】catchcatch說明:對大段代碼進(jìn)行try-catch,使程序無法根據(jù)不同的異常做出正確的應(yīng)激反應(yīng),也不利于定位問題,這是一種不負(fù)責(zé)任的表現(xiàn)。正例:用戶注冊的場景中,如果用戶輸入非法字符,或用戶名稱已存在,或用戶輸入密碼過于簡單,在程序上作出分門別類的判斷,并提示給用戶。【強(qiáng)制】解的內(nèi)容?!緩?qiáng)制】trycatch滾事務(wù)?!緩?qiáng)制】finallytry-catch。說明:如果JDK7及以上,可以使用try-with-resources方式。【強(qiáng)制】finallyreturn。說明:try塊中的return語句執(zhí)行成功后,并不馬上返回,而是繼續(xù)執(zhí)行finally塊中的語句,如果此處存在return語句,則在此直接返回,無情丟棄掉try塊中的返回點(diǎn)。反例:privateintx=0;publicintcheckReturn(){try{//x等于1,此處不返回return++x;}finally{//返回的結(jié)果是2return++x;}}【強(qiáng)制】捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類。說明:如果預(yù)期對方拋的是繡球,實(shí)際接到的是鉛球,就會產(chǎn)生意外情況。【強(qiáng)制】RPCThrowable類來進(jìn)行攔截。說明:通過反射機(jī)制來調(diào)用方法,如果找不到方法,拋出NoSuchMethodException。什么情況會拋出NoSuchMethodError呢?二方包在類沖突時,仲裁機(jī)制可能導(dǎo)致引入非預(yù)期的版本使類的方法簽名不匹配,或者在字節(jié)碼修改框架(比如:ASM)動態(tài)創(chuàng)建或修改類時,修改了相應(yīng)的方法簽名。這些情況,即使代碼編譯期是正確的,但在代碼運(yùn)行期時,會拋出NoSuchMethodError。【推薦】方法的nullnull值。說明:本手冊明確防止NPE是調(diào)用者的責(zé)任。即使被調(diào)用方法返回空集合或者空對象,對調(diào)用者來說,也并非高枕無憂,必須考慮到遠(yuǎn)程調(diào)用失敗、序列化失敗、運(yùn)行時異常等場景返回null的情況。【推薦】NPENPE產(chǎn)生的場景:返回類型為基本數(shù)據(jù)類型,returnNPE反例:publicintfreturnInteger對象},如果為nullNPE。null。isNotEmptynull。4)NPE。SessionNPEobj.getA().getB().getCNPE。正例:JDK8OptionalNPE【推薦】unchecked/checked異常,避免直接拋出newRuntimeException(),ExceptionThrowable,/ServiceException【參考】http/apiRPCResultisSuccess()方法、“錯誤碼”、“錯誤簡短信息”。說明:關(guān)于RPC方法返回方式使用Result方式的理由:使用拋異常返回方式,調(diào)用方如果沒有捕獲到就會產(chǎn)生運(yùn)行時錯誤。newerrormessage,對于調(diào)用端解決問題【參考】避免出現(xiàn)重復(fù)的代碼(Don'tRepeatYourself)DRY原則。說明:隨意復(fù)制和粘貼代碼,必然會導(dǎo)致代碼的重復(fù),在以后需要修改時,需要修改所有的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是組件化。正例:一個類中有多個public方法,都需要進(jìn)行數(shù)行相同的參數(shù)校驗(yàn)操作,這個時候請抽取:privatebooleancheckParam(DTOdto){...}(二) 日志規(guī)約【強(qiáng)制】應(yīng)用中不可直接使用日志系統(tǒng)(Log4jLogback)API,而應(yīng)依賴使用日志框架SLF4JAPIimportorg.slf4j.Logger;importorg.slf4j.LoggerFactory;privatestaticfinalLoggerlogger=LoggerFactory.getLogger(Test.class);【強(qiáng)制】15運(yùn)行狀態(tài)、安全相關(guān)信息、系統(tǒng)監(jiān)測、管理后臺操作、用戶敏感操作需要留存相關(guān)的網(wǎng)絡(luò)日6【強(qiáng)制】應(yīng)用中的擴(kuò)展日志(如打點(diǎn)、臨時監(jiān)控、訪問日志等)命名方式:appName_logType_logName.log。logType:stats/monitor/accesslogName:日志類查找。說明:推薦對日志進(jìn)行分類,如將錯誤日志和業(yè)務(wù)日志分開存放,便于開發(fā)人員查看,也便于通過日志對系統(tǒng)進(jìn)行及時監(jiān)控。正例:force-web應(yīng)用中單獨(dú)監(jiān)控時區(qū)轉(zhuǎn)換異常,如:force_web_timeZoneConvert.log【強(qiáng)制】在日志輸出時,字符串變量之間的拼接使用占位符的方式。說明:因?yàn)镾tring字符串的拼接會使用StringBuilder的append()方式,有一定的性能損耗。使用占位符僅是替換動作,可以有效提升性能。正例:logger.debug("Processingtradewithid:{}andsymbol:{}",id,symbol);【強(qiáng)制】trace/debug/info說明:debug(參數(shù))isDisabled(Level.DEBUG_INT)為真時(Slf4j的常見實(shí)Log4jLogback),returngetName()正例://tracedebug級別的日志if(logger.isDebugEnabled()){logger.debug("CurrentIDis:{}andnameis:{}",id,getName());}【強(qiáng)制】log4j.xmladditivity=false。正例:<loggername="com.taobao.dubbo.config"additivity="false">【強(qiáng)制】throws往上拋出。正例:logger.error(各類參數(shù)或者對象toString()+"_"+e.getMessage(),e);【推薦】debug日志;info日志;如果使warn撐爆,并記得及時刪除這些觀察日志。說明:大量地輸出無效日志,不利于系統(tǒng)性能提升,也不利于快速定位錯誤點(diǎn)。記錄日志時請思考:這些日志真的有人看嗎?看到這條日志你能做什么?能不能給問題排查帶來好處?【推薦】warnerror說明:注意日志輸出的級別,error級別只記錄系統(tǒng)邏輯出錯、異常或者重要的錯誤信息?!就扑]】用中文描述即可,否則容易產(chǎn)生歧義。【強(qiáng)制】問題,使用全英文來注釋和描述日志錯誤信息。三、單元測試【強(qiáng)制】AIR原則。說明:單元測試在線上運(yùn)行時,感覺像空氣(AIR)一樣并不存在,但在測試質(zhì)量的保障上,卻是非常關(guān)鍵的。好的單元測試宏觀上來說,具有自動化、獨(dú)立性、可重復(fù)執(zhí)行的特點(diǎn)。A:Automatic(自動化)I:Independent(獨(dú)立性)R:Repeatable(可重復(fù))【強(qiáng)制】System.outassert來驗(yàn)證?!緩?qiáng)制】間決不能互相調(diào)用,也不能依賴執(zhí)行的先后次序。反例:method2需要依賴method1的執(zhí)行,將執(zhí)行結(jié)果作為method2的輸入?!緩?qiáng)制】單元測試是可以重復(fù)執(zhí)行的,不能受到外界環(huán)境的影響。說明:單元測試通常會被放到持續(xù)集成中,每次有代碼checkin時單元測試都會被執(zhí)行。如果單測對外部環(huán)境(網(wǎng)絡(luò)、服務(wù)、中間件等)有依賴,容易導(dǎo)致持續(xù)集成機(jī)制的不可用。正例:為了不受外界環(huán)境影響,要求設(shè)計代碼時就把SUT的依賴改成注入,在測試時用spring這樣的DI框架注入一個本地(內(nèi)存)實(shí)現(xiàn)或者M(jìn)ock實(shí)現(xiàn)。【強(qiáng)制】級別,一般是方法級別。說明:只有測試粒度小才能在出錯時盡快定位到出錯位置。單測不負(fù)責(zé)檢查跨類或者跨系統(tǒng)的交互邏輯,那是集成測試的領(lǐng)域?!緩?qiáng)制】核心業(yè)務(wù)、核心應(yīng)用、核心模塊的增量代碼確保單元測試通過。說明:新增代碼及時補(bǔ)充單元測試,如果新增代碼影響了原有單元測試,請及時修正?!緩?qiáng)制】單元測試代碼必須寫在如下工程目錄:src/test/java,不允許寫在業(yè)務(wù)代碼目錄下。說明:源碼編譯時會跳過此目錄,而單元測試框架默認(rèn)是掃描此目錄。【推薦】70%100%說明:在工程規(guī)約的應(yīng)用分層中提到的DAO層,Manager層,可重用度高的Service,都應(yīng)該進(jìn)行單元測試?!就扑]】BCDE原則,以保證被測試模塊的交付質(zhì)量。B:Border,邊界值測試,包括循環(huán)邊界、特殊取值、特殊時間點(diǎn)、數(shù)據(jù)順序等。C:Correct,正確的輸入,并得到預(yù)期的結(jié)果。D:Design,與設(shè)計文檔相結(jié)合,來編寫單元測試。E:Error,強(qiáng)制錯誤信息輸入(如:非法數(shù)據(jù)、異常流程、業(yè)務(wù)允許外等),并得到預(yù)期的結(jié)果?!就扑]】或者直接操作數(shù)據(jù)庫把數(shù)據(jù)插入進(jìn)去,請使用程序插入或者導(dǎo)入數(shù)據(jù)的方式來準(zhǔn)備數(shù)據(jù)。反例:刪除某一行數(shù)據(jù)的單元測試,在數(shù)據(jù)庫中,先直接手動增加一行作為刪除目標(biāo),但是這一行新增數(shù)據(jù)并不符合業(yè)務(wù)插入規(guī)則,導(dǎo)致測試結(jié)果異常?!就扑]】對單元測試產(chǎn)生的數(shù)據(jù)有明確的前后綴標(biāo)識。正例:在企業(yè)智能事業(yè)部的內(nèi)部單元測試中,使用ENTERPRISE_INTELLIGENCE_UNIT_TEST_的前綴來標(biāo)識單元測試相關(guān)代碼?!就扑]】試要求而書寫不規(guī)范測試代碼?!就扑]】覆蓋所有測試用例。【推薦】補(bǔ)充單元測試用例?!緟⒖肌繛榱烁奖愕剡M(jìn)行單元測試,業(yè)務(wù)代碼應(yīng)避免以下情況:構(gòu)造方法中做的事情過多。存在過多的全局變量和靜態(tài)方法。存在過多的外部依賴。存在過多的條件語句。說明:多層條件語句建議使用衛(wèi)語句、策略模式、狀態(tài)模式等方式重構(gòu)?!緟⒖肌坎灰獙卧獪y試存在如下誤解:那是測試同學(xué)干的事情。本文是開發(fā)手冊,凡是本文內(nèi)容都是與開發(fā)同學(xué)強(qiáng)相關(guān)的。單元測試代碼是多余的。系統(tǒng)的整體功能與各單元部件的測試正常與否是強(qiáng)相關(guān)的。單元測試代碼不需要維護(hù)。一年半載后,那么單元測試幾乎處于廢棄狀態(tài)。單元測試與線上故障沒有辯證關(guān)系。好的單元測試能夠最大限度地規(guī)避線上故障。四、安全規(guī)約【強(qiáng)制】隸屬于用戶個人的頁面或者功能必須進(jìn)行權(quán)限控制校驗(yàn)。說明:防止沒有做水平權(quán)限校驗(yàn)就可隨意訪問、修改、刪除別人的數(shù)據(jù),比如查看他人的私信內(nèi)容、修改他人的訂單?!緩?qiáng)制】用戶敏感數(shù)據(jù)禁止直接展示,必須對展示數(shù)據(jù)進(jìn)行脫敏。說明:中國大陸個人手機(jī)號碼顯示為:137****09694【強(qiáng)制】SQLMETADATASQL注SQL訪問數(shù)據(jù)庫?!緩?qiáng)制】用戶請求傳入的任何參數(shù)必須做有效性驗(yàn)證。說明:忽略參數(shù)校驗(yàn)可能導(dǎo)致:pagesizeorderby任意重定向SQL反序列化注入ReDoS說明:Java代碼用正則來驗(yàn)證客戶端的輸入,有些正則寫法驗(yàn)證普通用戶輸入沒有問題,但是如果攻擊人員使用的是特殊構(gòu)造的字符串來驗(yàn)證,有可能導(dǎo)致死循環(huán)的結(jié)果?!緩?qiáng)制】HTML頁面輸出未經(jīng)安全過濾或未正確轉(zhuǎn)義的用戶數(shù)據(jù)?!緩?qiáng)制】表單、AJAXCSRF安全驗(yàn)證。說明:CSRF(Cross-siterequestforgery)跨站請求偽造是一類常見編程漏洞。對于存在CSRF漏洞的應(yīng)用/網(wǎng)站,攻擊者可以事先構(gòu)造好URL,只要受害者用戶一訪問,后臺便在用戶不知情的情況下對數(shù)據(jù)庫中用戶參數(shù)進(jìn)行相應(yīng)修改?!緩?qiáng)制】機(jī)制,如數(shù)量限制、疲勞度控制、驗(yàn)證碼校驗(yàn),避免被濫刷而導(dǎo)致資損。說明:如注冊時發(fā)送驗(yàn)證碼到手機(jī),如果沒有限制次數(shù)和頻率,那么可以利用此功能騷擾到其它用戶,并造成短信平臺資源浪費(fèi)?!就扑]】過濾等風(fēng)控策略。五、MySQL數(shù)據(jù)庫(一) 建表規(guī)約【強(qiáng)制】is_xxxunsignedtinyint(1,0)。說明:任何字段如果為非負(fù)數(shù),必須是unsigned。注意:POJO類中的任何布爾類型的變量,都不要加is前綴,所以,需要在<resultMap>設(shè)置從is_xxx到Xxx的映射關(guān)系。數(shù)據(jù)庫表示是與否的值,使用tinyint類型,堅(jiān)持is_xxx的命名方式是為了明確其取值含義與取值范圍。正例:表達(dá)邏輯刪除的字段名is_deleted,1表示刪除,0表示未刪除?!緩?qiáng)制】表名、字段名必須使用小寫字母或數(shù)字,禁止出現(xiàn)數(shù)字開頭,禁止兩個下劃線中間考慮。說明:MySQL在Windows下不區(qū)分大小寫,但在Linux下默認(rèn)是區(qū)分大小寫。因此,數(shù)據(jù)庫名、表名、字段名,都不允許出現(xiàn)任何大寫字母,避免節(jié)外生枝。正例:aliyun_admin,rdc_config,level3_name反例:AliyunAdmin,rdcConfig,level_3_name【強(qiáng)制】表名不使用復(fù)數(shù)名詞。說明:表名應(yīng)該僅僅表示表里面的實(shí)體內(nèi)容,不應(yīng)該表示實(shí)體數(shù)量,對應(yīng)于DO類名也是單數(shù)形式,符合表達(dá)習(xí)慣?!緩?qiáng)制】desc、range、match、delayedMySQL【強(qiáng)制】pk_uk_字段名;idx_字段名。說明:pk_即primarykey;uk_即uniquekey;idx_即index的簡稱?!緩?qiáng)制】decimalfloat和double。說明:在存儲的時候,float和double都存在精度損失的問題,很可能在比較值的時候,得到不正確的結(jié)果。如果存儲的數(shù)據(jù)范圍超過decimal的范圍,建議將數(shù)據(jù)拆成整數(shù)和小數(shù)并分開存儲。【強(qiáng)制】char定長字符串類型。【強(qiáng)制】varchar5000text【強(qiáng)制】表必備三字段:id,create_time,update_time。說明:其中id必為主鍵,類型為bigintunsi

溫馨提示

  • 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)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論