精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

白話(huà)阿里巴巴Java開(kāi)發(fā)手冊(cè)(編程規(guī)約)

開(kāi)發(fā) 開(kāi)發(fā)工具 后端
阿里巴巴發(fā)布了《阿里巴巴Java開(kāi)發(fā)手冊(cè)》,總結(jié)了阿里人多年一線(xiàn)實(shí)戰(zhàn)中積累的研發(fā)流程規(guī)范,這些流程規(guī)范在一定程度上能夠保證最終的項(xiàng)目交付質(zhì)量,通過(guò)限制開(kāi)發(fā)人員的編程風(fēng)格、實(shí)現(xiàn)方式來(lái)避免研發(fā)人員在實(shí)踐中容易犯的錯(cuò)誤,同樣的問(wèn)題大家使用同樣的模式解決,便于后期維護(hù)和擴(kuò)展,確保最終在大規(guī)模協(xié)作的項(xiàng)目中達(dá)成既定目標(biāo)。

 [[190079]]

最近,阿里巴巴發(fā)布了《阿里巴巴Java開(kāi)發(fā)手冊(cè)》,總結(jié)了阿里人多年一線(xiàn)實(shí)戰(zhàn)中積累的研發(fā)流程規(guī)范,這些流程規(guī)范在一定程度上能夠保證最終的項(xiàng)目交付質(zhì)量,通過(guò)限制開(kāi)發(fā)人員的編程風(fēng)格、實(shí)現(xiàn)方式來(lái)避免研發(fā)人員在實(shí)踐中容易犯的錯(cuò)誤,同樣的問(wèn)題大家使用同樣的模式解決,便于后期維護(hù)和擴(kuò)展,確保最終在大規(guī)模協(xié)作的項(xiàng)目中達(dá)成既定目標(biāo)。

無(wú)獨(dú)有偶,筆者去年在公司里負(fù)責(zé)升級(jí)和制定研發(fā)流程、設(shè)計(jì)模板、設(shè)計(jì)標(biāo)準(zhǔn)、代碼標(biāo)準(zhǔn)等規(guī)范,并在實(shí)際工作中進(jìn)行了應(yīng)用和推廣,收效頗豐,也總結(jié)了適合支付平臺(tái)的技術(shù)規(guī)范,由于阿里巴巴Java開(kāi)發(fā)手冊(cè)本身定位為規(guī)約和規(guī)范,語(yǔ)言簡(jiǎn)單、精煉,沒(méi)有太多的解讀和示例,有些條款對(duì)于一般開(kāi)發(fā)人員理解起來(lái)比較困難,本文借著阿里巴巴發(fā)布的Java開(kāi)發(fā)手冊(cè),詳細(xì)解讀Java平臺(tái)下開(kāi)發(fā)規(guī)范和標(biāo)準(zhǔn)的制定和實(shí)施,強(qiáng)調(diào)那些在開(kāi)發(fā)過(guò)程中需要重點(diǎn)關(guān)注的技術(shù)點(diǎn),特別是解決某類(lèi)已識(shí)別問(wèn)題的模式和反模式。

《阿里巴巴Java開(kāi)發(fā)手冊(cè)》分為編程規(guī)約、異常日志、MySQL規(guī)約、工程規(guī)約、安全規(guī)約五大部分,本系列文章以這五部分主題為主線(xiàn),分為五篇文章發(fā)布,本文為系列文章的第一篇-編程規(guī)約,后續(xù)會(huì)盡快發(fā)布其余的文章。

1 命名規(guī)約

1.【強(qiáng)制】 代碼中的命名均不能以下劃線(xiàn)或美元符號(hào)開(kāi)始,也不能以下劃線(xiàn)或美元符號(hào)結(jié)束。

反例: name / __name / $Object / name / name$ / Object$

白話(huà):

這條不夠嚴(yán)格,普通的變量、類(lèi)名、方法名必須使用駝峰式命名,最好不要使用下劃線(xiàn)和美元符號(hào),否則看起來(lái)像腳本語(yǔ)言似得,常量可以使用下劃線(xiàn),但是也不要放在常量開(kāi)頭和結(jié)尾。

2.【強(qiáng)制】 代碼中的命名嚴(yán)禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。

說(shuō)明: 正確的英文拼寫(xiě)和語(yǔ)法可以讓閱讀者易于理解,避免歧義。注意,即使純拼音命名方式 也要避免采用。

反例: DaZhePromotion [打折] / getPingfenByName() [評(píng)分] / int 某變量 = 3

正例: alibaba / taobao / youku / hangzhou 等國(guó)際通用的名稱(chēng),可視同英文。

白話(huà):

中英混合的人種咱不歧視,變量名混合太丑了。

Java編譯器支持Unicode(UTF-8),允許中文命名變量,不過(guò)打中文還是沒(méi)有英文快。

英文!英文起名,洋氣、大方、高大上...

3.【強(qiáng)制】類(lèi)名使用 UpperCamelCase 風(fēng)格,必須遵從駝峰形式,但以下情形例外:(領(lǐng)域模型 的相關(guān)命名)DO / BO / DTO / VO等。

正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion

反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion

白話(huà):

約定俗成的名稱(chēng)或者縮寫(xiě)例外。

4.【強(qiáng)制】方法名、參數(shù)名、成員變量、局部變量都統(tǒng)一使用 lowerCamelCase 風(fēng)格,必須遵從駝峰形式。

正例: localValue / getHttpMessage() / inputUserId

白話(huà):

約定俗稱(chēng)的名稱(chēng)或者縮寫(xiě)例外。

ID為簡(jiǎn)寫(xiě),Id和ID均可。

5.【強(qiáng)制】常量命名全部大寫(xiě),單詞間用下劃線(xiàn)隔開(kāi),力求語(yǔ)義表達(dá)完整清楚,不要嫌名字長(zhǎng)。

正例: MAX_STOCK_COUNT

反例: MAX_COUNT

白話(huà):

必須全部大寫(xiě),除了字母數(shù)字只可以使用下劃線(xiàn),并且不能用在開(kāi)頭和結(jié)尾。

6.【強(qiáng)制】抽象類(lèi)命名使用 Abstract 或 Base 開(kāi)頭;異常類(lèi)命名使用 Exception 結(jié)尾;測(cè)試類(lèi)命名以它要測(cè)試的類(lèi)的名稱(chēng)開(kāi)始,以 Test 結(jié)尾。

白話(huà):

家里放一瓶敵敵畏,上面不寫(xiě)標(biāo)簽,萬(wàn)一喝大了、渴了、喝了、就慘了,你懂的。

7.【強(qiáng)制】中括號(hào)是數(shù)組類(lèi)型的一部分,數(shù)組定義如下:String[] args;

反例: 使用String args[]的方式來(lái)定義。

白話(huà):

這種語(yǔ)法編譯器也認(rèn),但是我們畢竟寫(xiě)Java程序,而不是寫(xiě)C/C++程序。

這怪Java編譯器小組,一開(kāi)始就不應(yīng)該支持這種語(yǔ)法。

8.【強(qiáng)制】POJO 類(lèi)中布爾類(lèi)型的變量,都不要加 is,否則部分框架解析會(huì)引起序列化錯(cuò)誤。

反例: 定義為基本數(shù)據(jù)類(lèi)型Boolean isSuccess;的屬性,它的方法也是isSuccess(),RPC 框架在反向解析的時(shí)候,“以為”對(duì)應(yīng)的屬性名稱(chēng)是 success,導(dǎo)致屬性獲取不到,進(jìn)而拋出異常。

白話(huà):

一些框架使用getter和setter做序列化,有的根據(jù)屬性本身取值,帶了is前綴就找不到了,變量名不要帶be動(dòng)詞,語(yǔ)法不對(duì),英文補(bǔ)考!

9.【強(qiáng)制】包名統(tǒng)一使用小寫(xiě),點(diǎn)分隔符之間有且僅有一個(gè)自然語(yǔ)義的英語(yǔ)單詞。包名統(tǒng)一使用 單數(shù)形式,但是類(lèi)名如果有復(fù)數(shù)含義,類(lèi)名可以使用復(fù)數(shù)形式。

正例: 應(yīng)用工具類(lèi)包名為com.alibaba.open.util、類(lèi)名為MessageUtils(此規(guī)則參考 spring 的框架結(jié)構(gòu))

白話(huà):

包名大寫(xiě)、帶下劃線(xiàn)等,不專(zhuān)業(yè)、難看、不高大上。

10.【強(qiáng)制】杜絕完全不規(guī)范的縮寫(xiě),避免望文不知義。

反例: AbstractClass“縮寫(xiě)”命名成 AbsClass;condition“縮寫(xiě)”命名成 condi,此類(lèi) 隨意縮寫(xiě)嚴(yán)重降低了代碼的可閱讀性。

白話(huà):

不要太摳,不是太長(zhǎng)的名字直接寫(xiě)上就好,編譯器編譯優(yōu)化后變量名將不存在,會(huì)編譯成相對(duì)于方法堆棧bp指針地址的相對(duì)地址,長(zhǎng)變量名不會(huì)占用更多空間。

英文中的縮寫(xiě)有個(gè)慣例,去掉元音留下輔音即可,不能亂縮寫(xiě)。

11.【推薦】如果使用到了設(shè)計(jì)模式,建議在類(lèi)名中體現(xiàn)出具體模式。

說(shuō)明: 將設(shè)計(jì)模式體現(xiàn)在名字中,有利于閱讀者快速理解架構(gòu)設(shè)計(jì)思想。

正例: public class OrderFactory;

  1. public class LoginProxy;  
  2.  public class ResourceObserver; 

白話(huà):

讓全世界都知道你會(huì)設(shè)計(jì)模式,這是個(gè)崇尚顯擺的社會(huì)。

12.【推薦】接口類(lèi)中的方法和屬性不要加任何修飾符號(hào)(public 也不要加),保持代碼的簡(jiǎn)潔 性,并加上有效的 Javadoc 注釋。盡量不要在接口里定義變量,如果一定要定義變量,肯定是與接口方法相關(guān),并且是整個(gè)應(yīng)用的基礎(chǔ)常量。

正例: 接口方法簽名:void f();

13.接口基礎(chǔ)常量表示:String COMPANY = "alibaba";

反例: 接口方法定義:public abstract void f();

說(shuō)明:JDK8 中接口允許有默認(rèn)實(shí)現(xiàn),那么這個(gè) default 方法,是對(duì)所有實(shí)現(xiàn)類(lèi)都有價(jià)值的默 認(rèn)實(shí)現(xiàn)。

白話(huà):

脫了褲子放屁始終有點(diǎn)麻煩。

13.接口和實(shí)現(xiàn)類(lèi)的命名有兩套規(guī)則:

1)【強(qiáng)制】對(duì)于 Service 和 DAO 類(lèi),基于 SOA 的理念,暴露出來(lái)的服務(wù)一定是接口,內(nèi)部的實(shí)現(xiàn)類(lèi)用 Impl 的后綴與接口區(qū)別。

正例: CacheServiceImpl 實(shí)現(xiàn) CacheService 接口。

2)【推薦】 如果是形容能力的接口名稱(chēng),取對(duì)應(yīng)的形容詞做接口名(通常是–able 的形式)。

正例: AbstractTranslator 實(shí)現(xiàn) Translatable。

白話(huà):

嚴(yán)重同意!可是想想Observer和Observable,我就不說(shuō)話(huà)了。

14.【參考】枚舉類(lèi)名建議帶上 Enum 后綴,枚舉成員名稱(chēng)需要全大寫(xiě),單詞間用下劃線(xiàn)隔開(kāi)。

說(shuō)明: 枚舉其實(shí)就是特殊的常量類(lèi),且構(gòu)造方法被默認(rèn)強(qiáng)制是私有。

正例: 枚舉名字:DealStatusEnum,成員名稱(chēng): SUCCESS / UNKOWN_REASON。

白話(huà):

不要駝峰!記住枚舉不要駝峰!總是有好多人枚舉用駝峰。

15.【參考】各層命名規(guī)約:

A) Service/DAO層方法命名規(guī)約

1) 獲取單個(gè)對(duì)象的方法用get做前綴。

2) 獲取多個(gè)對(duì)象的方法用list做前綴。

3) 獲取統(tǒng)計(jì)值的方法用count做前綴。

4) 插入的方法用save(推薦)或insert做前綴。

5) 刪除的方法用remove(推薦)或delete做前綴。

6) 修改的方法用update做前綴。

B) 領(lǐng)域模型命名規(guī)約

1) 數(shù)據(jù)對(duì)象:xxxDO,xxx即為數(shù)據(jù)表名。

2) 數(shù)據(jù)傳輸對(duì)象:xxxDTO,xxx為業(yè)務(wù)領(lǐng)域相關(guān)的名稱(chēng)。

3) 展示對(duì)象:xxxVO,xxx一般為網(wǎng)頁(yè)名稱(chēng)。

4) POJO是DO/DTO/BO/VO的統(tǒng)稱(chēng),禁止命名成xxxPOJO。

白話(huà):

大家都這么認(rèn)為很重要。

2 常量定義

1.【強(qiáng)制】不允許出現(xiàn)任何魔法值(即未經(jīng)定義的常量)直接出現(xiàn)在代碼中。

反例: String key = "Id#taobao_"+tradeId; cache.put(key, value);

白話(huà):

這個(gè)不用說(shuō)了,隨地吐痰和隨地大小便是不應(yīng)該的,新加坡是要鞭刑的!

2.【強(qiáng)制】long 或者 Long 初始賦值時(shí),必須使用大寫(xiě)的 L,不能是小寫(xiě)的 l,小寫(xiě)容易跟數(shù)字 1 混淆,造成誤解。

說(shuō)明: Long a = 2l; 寫(xiě)的是數(shù)字的21,還是Long型的2?

白話(huà):

看看區(qū)塊鏈中用了base58,而不是base64,秒懂什么是從用戶(hù)角度考慮產(chǎn)品設(shè)計(jì)!

3.【推薦】不要使用一個(gè)常量類(lèi)維護(hù)所有常量,應(yīng)該按常量功能進(jìn)行歸類(lèi),分開(kāi)維護(hù)。如:緩存相關(guān)的常量放在類(lèi): CacheConsts 下; 系統(tǒng)配置相關(guān)的常量放在類(lèi): ConfigConsts 下。

說(shuō)明: 大而全的常量類(lèi),非得使用查找功能才能定位到修改的常量,不利于理解和維護(hù)。

白話(huà):

盡量讓功能自閉包,標(biāo)準(zhǔn)是一個(gè)小模塊拷貝出去直接就能用,而不是缺這缺那的,是不是讀者很多時(shí)候拷貝了一套類(lèi),運(yùn)行時(shí)候發(fā)現(xiàn)不能用,缺常量,把常量類(lèi)拷貝過(guò)來(lái),發(fā)現(xiàn)常量類(lèi)中有很多不相關(guān)的常量,還得清理。

4.【推薦】常量的復(fù)用層次有五層: 跨應(yīng)用共享常量、應(yīng)用內(nèi)共享常量、子工程內(nèi)共享常量、包內(nèi)共享常量、類(lèi)內(nèi)共享常量。

1) 跨應(yīng)用共享常量: 放置在二方庫(kù)中,通常是client.jar中的constant目錄下。

2) 應(yīng)用內(nèi)共享常量: 放置在一方庫(kù)的modules中的constant目錄下。

反例: 易懂變量也要統(tǒng)一定義成應(yīng)用內(nèi)共享常量,兩位攻城師在兩個(gè)類(lèi)中分別定義了表示 “是”的變量:

類(lèi)A中: public static final String YES = "yes";

類(lèi)B中: public static final String YES = "y"; A.YES.equals(B.YES),預(yù)期是 true,但實(shí)際返回為 false,導(dǎo)致產(chǎn)生線(xiàn)上問(wèn)題。

3) 子工程內(nèi)部共享常量: 即在當(dāng)前子工程的constant目錄下。

4) 包內(nèi)共享常量: 即在當(dāng)前包下單獨(dú)的constant目錄下。

5) 類(lèi)內(nèi)共享常量: 直接在類(lèi)內(nèi)部private static final定義。

白話(huà):

一方庫(kù)、二方庫(kù)、三方庫(kù),叫法很專(zhuān)業(yè),放在離自己最近的上面一個(gè)層次即可。

5.【推薦】如果變量值僅在一個(gè)范圍內(nèi)變化用 Enum 類(lèi)。如果還帶有名稱(chēng)之外的延伸屬性,必須 使用 Enum 類(lèi),下面正例中的數(shù)字就是延伸信息,表示星期幾。

正例: public Enum { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);}

白話(huà):

枚舉值需要定義延伸屬性的場(chǎng)景通常是要持久數(shù)據(jù)庫(kù),或者顯示在界面上。

3 格式規(guī)約

1.【強(qiáng)制】大括號(hào)的使用約定。如果是大括號(hào)內(nèi)為空,則簡(jiǎn)潔地寫(xiě)成{}即可,不需要換行; 如果 是非空代碼塊則:

1) 左大括號(hào)前不換行。

2) 左大括號(hào)后換行。

3) 右大括號(hào)前換行。

4) 右大括號(hào)后還有else等代碼則不換行;表示終止右大括號(hào)后必須換行。

白話(huà):

好風(fēng)格,討厭那種左大括號(hào)前換行的,看不慣。

2.【強(qiáng)制】 左括號(hào)和后一個(gè)字符之間不出現(xiàn)空格; 同樣,右括號(hào)和前一個(gè)字符之間也不出現(xiàn)空 格。詳見(jiàn)第 5 條下方正例提示。

白話(huà):

程序?qū)懲昕梢杂镁庉嬈鞯母袷交δ芨袷交珽clipse中快捷鍵是shift+alt+f,筆者寫(xiě)程序的時(shí)候有個(gè)習(xí)慣,每次謝了一段代碼都會(huì)按ctrl+alt+o、ctrl+alt+f、ctrl+s,相信會(huì)有相同習(xí)慣的同行。

3.【強(qiáng)制】if/for/while/switch/do 等保留字與左右括號(hào)之間都必須加空格。

白話(huà):

程序?qū)懲昕梢杂镁庉嬈鞯母袷交δ芨袷交珽clipse中快捷鍵是shift+alt+f,筆者寫(xiě)程序的時(shí)候有個(gè)習(xí)慣,每次謝了一段代碼都會(huì)按ctrl+alt+o、ctrl+alt+f、ctrl+s,相信會(huì)有相同習(xí)慣的同行。

4.【強(qiáng)制】任何運(yùn)算符左右必須加一個(gè)空格。

說(shuō)明: 運(yùn)算符包括賦值運(yùn)算符=、邏輯運(yùn)算符&&、加減乘除符號(hào)、三目運(yùn)算符等。

白話(huà):

程序?qū)懲昕梢杂镁庉嬈鞯母袷交δ芨袷交珽clipse中快捷鍵是shift+alt+f,筆者寫(xiě)程序的時(shí)候有個(gè)習(xí)慣,每次謝了一段代碼都會(huì)按ctrl+alt+o、ctrl+alt+f、ctrl+s,相信會(huì)有相同習(xí)慣的同行。

5.【強(qiáng)制】縮進(jìn)采用 4 個(gè)空格,禁止使用 tab 字符。

說(shuō)明: 如果使用 tab 縮進(jìn),必須設(shè)置 1 個(gè) tab 為 4 個(gè)空格。IDEA 設(shè)置 tab 為 4 個(gè)空格時(shí),請(qǐng)勿勾選Use tab character; 而在 eclipse 中,必須勾選 insert spaces for tabs。

正例: (涉及1-5點(diǎn))

  1. public static void main(String[] args) { 
  2.      // 縮進(jìn) 4 個(gè)空格 
  3.      String say = "hello"
  4.      // 運(yùn)算符的左右必須有一個(gè)空格 
  5.      int flag = 0; 
  6.      // 關(guān)鍵詞 if 與括號(hào)之間必須有一個(gè)空格,括號(hào)內(nèi)的 f 與左括號(hào),0 與右括號(hào)不需要空格      
  7.      if (flag == 0) { 
  8.          System.out.println(say); 
  9.      } 
  10.      // 左大括號(hào)前加空格且不換行;左大括號(hào)后換行  
  11.      if (flag == 1) { 
  12.          System.out.println("world"); 
  13.          // 右大括號(hào)前換行,右大括號(hào)后有 else,不用換行 
  14.      } else { System.out.println("ok"); 
  15.          // 在右大括號(hào)后直接結(jié)束,則必須換行 
  16.      } 
  17.  } 

白話(huà):

這樣看慣了,怎么看怎么清晰。

6.【強(qiáng)制】單行字符數(shù)限制不超過(guò) 120 個(gè),超出需要換行,換行時(shí)遵循如下原則:

1) 第二行相對(duì)第一行縮進(jìn) 4 個(gè)空格,從第三行開(kāi)始,不再繼續(xù)縮進(jìn),參考示例。

2) 運(yùn)算符與下文一起換行。

3) 方法調(diào)用的點(diǎn)符號(hào)與下文一起換行。

4) 在多個(gè)參數(shù)超長(zhǎng),逗號(hào)后進(jìn)行換行。

5) 在括號(hào)前不要換行,見(jiàn)反例。

正例:

  1. StringBuffer sb = new StringBuffer(); 
  2. //超過(guò) 120 個(gè)字符的情況下,換行縮進(jìn) 4 個(gè)空格,并且方法前的點(diǎn)符號(hào)一起換行      
  3. sb.append("zi").append("xin")... 
  4.       .append("huang")... 
  5.       .append("huang")... 
  6.       .append("huang"); 

反例:

  1. StringBuffer sb = new StringBuffer(); 
  2. //超過(guò) 120 個(gè)字符的情況下,不要在括號(hào)前換行       
  3. sb.append("zi").append("xin")...append 
  4.     ("huang"); 
  5.  
  6. //參數(shù)很多的方法調(diào)用可能超過(guò) 120 個(gè)字符,不要在逗號(hào)前換行  
  7. method(args1,   args2, args3, ... 
  8. , argsX); 

白話(huà):

一行代碼盡量不要寫(xiě)太長(zhǎng),長(zhǎng)了拆開(kāi)不就得了。

7.【強(qiáng)制】方法參數(shù)在定義和傳入時(shí),多個(gè)參數(shù)逗號(hào)后邊必須加空格。

正例: 下例中實(shí)參的"a", 后邊必須要有一個(gè)空格。

  1. method("a""b""c"); 

白話(huà):

不加空格太擠了,就像人沒(méi)長(zhǎng)開(kāi)似得。

8.【強(qiáng)制】IDE的text file encoding設(shè)置為UTF-8; IDE中文件的換行符使用Unix格式, 不要使用 windows 格式。

白話(huà):

請(qǐng)不要用GB字符集,換了環(huán)境總有問(wèn)題,Java程序多數(shù)跑在Linux上,當(dāng)然要用Unix換行符。

9.【推薦】沒(méi)有必要增加若干空格來(lái)使某一行的字符與上一行的相應(yīng)字符對(duì)齊。

正例:

  1. int a = 3;         
  2. long b = 4L;         
  3. float c = 5F;         
  4. StringBuffer sb = new StringBuffer(); 

說(shuō)明: 增加 sb 這個(gè)變量,如果需要對(duì)齊,則給 a、b、c 都要增加幾個(gè)空格,在變量比較多的 情況下,是一種累贅的事情。

白話(huà):

沒(méi)必要,沒(méi)必要,那樣反而不好看。

10.【推薦】方法體內(nèi)的執(zhí)行語(yǔ)句組、變量的定義語(yǔ)句組、不同的業(yè)務(wù)邏輯之間或者不同的語(yǔ)義

之間插入一個(gè)空行。相同業(yè)務(wù)邏輯和語(yǔ)義之間不需要插入空行。

說(shuō)明: 沒(méi)有必要插入多行空格進(jìn)行隔開(kāi)。

白話(huà):

和我的習(xí)慣一樣一樣的,一段邏輯空一行。

4 OOP 規(guī)約

1.【強(qiáng)制】避免通過(guò)一個(gè)類(lèi)的對(duì)象引用訪(fǎng)問(wèn)此類(lèi)的靜態(tài)變量或靜態(tài)方法,無(wú)謂增加編譯器解析成

本,直接用類(lèi)名來(lái)訪(fǎng)問(wèn)即可。

白話(huà):

也不直觀(guān),看調(diào)用代碼看不出來(lái)是靜態(tài)方法,容易誤解。

2.【強(qiáng)制】所有的覆寫(xiě)方法,必須加@Override 注解。

反例:getObject()與 get0bject()的問(wèn)題。一個(gè)是字母的 O,一個(gè)是數(shù)字的 0,加@Override 可以準(zhǔn)確判斷是否覆蓋成功。另外,如果在抽象類(lèi)中對(duì)方法簽名進(jìn)行修改,其實(shí)現(xiàn)類(lèi)會(huì)馬上編譯報(bào)錯(cuò)。

白話(huà):

Java和C++不一樣,C++是在父類(lèi)先聲明虛擬函數(shù)子類(lèi)才覆寫(xiě),Java是任何方法都能覆寫(xiě),也可以不覆寫(xiě),所以覆寫(xiě)不覆寫(xiě)是沒(méi)有編譯器檢查的,除非接口中某一個(gè)方法完全沒(méi)有被實(shí)現(xiàn)才會(huì)編譯報(bào)錯(cuò)。

3.【強(qiáng)制】相同參數(shù)類(lèi)型,相同業(yè)務(wù)含義,才可以使用 Java 的可變參數(shù),避免使用 Object。

說(shuō)明: 可變參數(shù)必須放置在參數(shù)列表的最后。(提倡同學(xué)們盡量不用可變參數(shù)編程)

正例: public User getUsers(String type, Integer... ids)

白話(huà):

用處不大,可以用重載方法或者數(shù)組參數(shù)代替。

一般應(yīng)用在日志的 API 定義上,用于傳不定的日志參數(shù)。

4.【強(qiáng)制】外部正在調(diào)用或者二方庫(kù)依賴(lài)的接口,不允許修改方法簽名,避免對(duì)接口調(diào)用方產(chǎn)生 影響。接口過(guò)時(shí)必須加@Deprecated 注解,并清晰地說(shuō)明采用的新接口或者新服務(wù)是什么。

白話(huà):

設(shè)計(jì)時(shí)沒(méi)有考慮周全,需要改造接口,需要通過(guò)增加新接口,遷移后下線(xiàn)老接口的方式實(shí)現(xiàn)。

REST接口只能增加參數(shù),不能減少參數(shù),返回值的內(nèi)容也是只增不減。

5.【強(qiáng)制】不能使用過(guò)時(shí)的類(lèi)或方法。

說(shuō)明: java.net.URLDecoder 中的方法 decode(String encodeStr) 這個(gè)方法已經(jīng)過(guò)時(shí),應(yīng)該使用雙參數(shù) decode(String source, String encode)。接口提供方既然明確是過(guò)時(shí)接口,那么有義務(wù)同時(shí)提供新的接口; 作為調(diào)用方來(lái)說(shuō),有義務(wù)去考證過(guò)時(shí)方法的新實(shí)現(xiàn)是什么。

白話(huà):

明確了責(zé)任和義務(wù),接口提供方也有義務(wù)推動(dòng)接口使用方盡早遷移,不要積累技術(shù)負(fù)債。

6.【強(qiáng)制】Object 的 equals 方法容易拋空指針異常,應(yīng)使用常量或確定有值的對(duì)象來(lái)調(diào)用 equals。

正例: "test".equals(object);

反例: object.equals("test");

說(shuō)明: 推薦使用java.util.Objects#equals (JDK7引入的工具類(lèi))

白話(huà):

常量比變量,永遠(yuǎn)都不變的原則。

7.【強(qiáng)制】所有的相同類(lèi)型的包裝類(lèi)對(duì)象之間值的比較,全部使用 equals 方法比較。

說(shuō)明: 對(duì)于Integer var = ?在-128至127之間的賦值,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)行判斷。

白話(huà):

Java世界里相等請(qǐng)用equals方法,==表示對(duì)象相等,一般在框架開(kāi)發(fā)中會(huì)用到。

關(guān)于基本數(shù)據(jù)類(lèi)型與包裝數(shù)據(jù)類(lèi)型的使用標(biāo)準(zhǔn)如下:

1) 【強(qiáng)制】所有的POJO類(lèi)屬性必須使用包裝數(shù)據(jù)類(lèi)型。

2) 【強(qiáng)制】RPC方法的返回值和參數(shù)必須使用包裝數(shù)據(jù)類(lèi)型。

3) 【推薦】所有的局部變量使用基本數(shù)據(jù)類(lèi)型。

說(shuō)明: POJO 類(lèi)屬性沒(méi)有初值是提醒使用者在需要使用時(shí),必須自己顯式地進(jìn)行賦值,任何

NPE 問(wèn)題,或者入庫(kù)檢查,都由使用者來(lái)保證。

正例: 數(shù)據(jù)庫(kù)的查詢(xún)結(jié)果可能是 null,因?yàn)樽詣?dòng)拆箱,用基本數(shù)據(jù)類(lèi)型接收有 NPE 風(fēng)險(xiǎn)。

反例: 比如顯示成交總額漲跌情況,即正負(fù) x%,x 為基本數(shù)據(jù)類(lèi)型,調(diào)用的 RPC 服務(wù),調(diào)用不成功時(shí),返回的是默認(rèn)值,頁(yè)面顯示:0%,這是不合理的,應(yīng)該顯示成中劃線(xiàn)-。所以包裝數(shù)據(jù)類(lèi)型的 null 值,能夠表示額外的信息,如:遠(yuǎn)程調(diào)用失敗,異常退出。

白話(huà):

其實(shí)包裝數(shù)據(jù)類(lèi)型與基本數(shù)據(jù)類(lèi)型相比,增加了一個(gè)null的狀態(tài),可以攜帶更多的語(yǔ)義。

9.【強(qiáng)制】定義 DO/DTO/VO 等 POJO 類(lèi)時(shí),不要設(shè)定任何屬性默認(rèn)值。

反例: POJO類(lèi)的gmtCreate默認(rèn)值為new Date(); 但是這個(gè)屬性在數(shù)據(jù)提取時(shí)并沒(méi)有置入具體值,在更新其它字段時(shí)又附帶更新了此字段,導(dǎo)致創(chuàng)建時(shí)間被修改成當(dāng)前時(shí)間。

白話(huà):

雖然這里反例不太容易看懂,但是要記得持久領(lǐng)域?qū)ο笾坝蓱?yīng)用層統(tǒng)一賦值gmtCreate和gmtModify字段。

10.【強(qiáng)制】序列化類(lèi)新增屬性時(shí),請(qǐng)不要修改 serialVersionUID 字段,避免反序列失敗; 如 果完全不兼容升級(jí),避免反序列化混亂,那么請(qǐng)修改 serialVersionUID 值。

說(shuō)明:注意 serialVersionUID 不一致會(huì)拋出序列化運(yùn)行時(shí)異常。

白話(huà):

不到萬(wàn)不得已不要使用JDK自身的序列化,機(jī)制很重,信息冗余有版本。

11.【強(qiáng)制】構(gòu)造方法里面禁止加入任何業(yè)務(wù)邏輯,如果有初始化邏輯,請(qǐng)放在 init 方法中。

白話(huà):

這樣做一種是規(guī)范,代碼清晰,還有就是異常堆棧上更容易識(shí)別出錯(cuò)的方法和語(yǔ)句。

12.【強(qiáng)制】POJO 類(lèi)必須寫(xiě) toString 方法。使用 IDE 的中工具:source> generate toString 時(shí),如果繼承了另一個(gè) POJO 類(lèi),注意在前面加一下 super.toString。

說(shuō)明: 在方法執(zhí)行拋出異常時(shí),可以直接調(diào)用 POJO 的 toString()方法打印其屬性值,便于排 查問(wèn)題。

白話(huà):

這里還有一個(gè)大坑,寫(xiě)toString的時(shí)候要保證不會(huì)發(fā)生NPE,有的時(shí)候toString調(diào)用實(shí)例變量的toString,實(shí)例變量由于某些原因?yàn)閚ull,導(dǎo)致NPE,代碼沒(méi)有處理好就終止,這個(gè)問(wèn)題坑了好多次。

13.【推薦】使用索引訪(fǎng)問(wèn)用 String 的 split 方法得到的數(shù)組時(shí),需做最后一個(gè)分隔符后有無(wú)內(nèi)容的檢查,否則會(huì)有拋 IndexOutOfBoundsException 的風(fēng)險(xiǎn)。

說(shuō)明:

  1. String str = "a,b,c,,";             
  2. String[] ary = str.split(","); //預(yù)期大于 3,結(jié)果是 3  
  3. System.out.println(ary.length); 

白話(huà):

編程要留心眼,任何不確定的地方都要判斷、處理,否則掉到坑里了自己爬出來(lái)很費(fèi)勁。

Java編程判空的思想要實(shí)施縈繞在每個(gè)開(kāi)發(fā)人員的腦海里。

14.【推薦】當(dāng)一個(gè)類(lèi)有多個(gè)構(gòu)造方法,或者多個(gè)同名方法,這些方法應(yīng)該按順序放置在一起, 便于閱讀。

白話(huà):

這規(guī)范說(shuō)的咋就和我的習(xí)慣一模一樣呢!

1.【推薦】 類(lèi)內(nèi)方法定義順序依次是: 公有方法或保護(hù)方法 > 私有方法 > getter/setter

方法。

說(shuō)明: 公有方法是類(lèi)的調(diào)用者和維護(hù)者最關(guān)心的方法,首屏展示最好; 保護(hù)方法雖然只是子類(lèi) 關(guān)心,也可能是“模板設(shè)計(jì)模式”下的核心方法; 而私有方法外部一般不需要特別關(guān)心,是一個(gè)黑盒實(shí)現(xiàn); 因?yàn)榉椒ㄐ畔r(jià)值較低,所有 Service 和 DAO 的 getter/setter 方法放在類(lèi)體最 后。

白話(huà):

我推薦把一套邏輯的共有方法、保護(hù)方法、私有方法放在一起,所有g(shù)etter/setter放在最后,這樣感覺(jué)更有邏輯!

2.【推薦】setter 方法中,參數(shù)名稱(chēng)與類(lèi)成員變量名稱(chēng)一致,this.成員名 = 參數(shù)名。在

getter/setter 方法中,盡量不要增加業(yè)務(wù)邏輯,增加排查問(wèn)題的難度。

反例:

  1. public Integer getData() {  
  2.     if (true) { 
  3.         return data + 100;  
  4.     } else { 
  5.         return data - 100; } 
  6.     } 

白話(huà):

雙手贊成。

3.【推薦】循環(huán)體內(nèi),字符串的連接方式,使用 StringBuilder 的 append 方法進(jìn)行擴(kuò)展。

反例:

  1. String str = "start"
  2.   for (int I = 0; I < 100; i++) { 
  3.       str = str + "hello";  
  4.   } 

說(shuō)明: 反編譯出的字節(jié)碼文件顯示每次循環(huán)都會(huì) new 出一個(gè) StringBuilder 對(duì)象,然后進(jìn)行 append 操作,最后通過(guò) toString 方法返回 String 對(duì)象,造成內(nèi)存資源浪費(fèi)。

白話(huà):

一定使用StringBuilder,不要使用StringBuffer,StringBuffer是線(xiàn)程安全的,太重。

我就一直想不明白Java編譯器為什么不做個(gè)優(yōu)化呢?

4.【推薦】下列情況,聲明成 final 會(huì)更有提示性:

1) 不需要重新賦值的變量,包括類(lèi)屬性、局部變量。

2) 對(duì)象參數(shù)前加final,表示不允許修改引用的指向。

3) 類(lèi)方法確定不允許被重寫(xiě)。

白話(huà):

盡量多使用final關(guān)鍵字,保證編譯器的校驗(yàn)機(jī)制起作用,也體現(xiàn)了“契約式編程”的思想。

5.【推薦】慎用 Object 的 clone 方法來(lái)拷貝對(duì)象。

說(shuō)明: 對(duì)象的 clone 方法默認(rèn)是淺拷貝,若想實(shí)現(xiàn)深拷貝需要重寫(xiě) clone 方法實(shí)現(xiàn)屬性對(duì)象的拷貝。

白話(huà):

最好是使用構(gòu)造函數(shù)來(lái)重新構(gòu)造對(duì)象,使用clone淺拷貝的時(shí)候,對(duì)象引用關(guān)系可能很復(fù)雜,不直觀(guān),不好理解。

6.【推薦】類(lèi)成員與方法訪(fǎng)問(wèn)控制從嚴(yán):

1) 如果不允許外部直接通過(guò)new來(lái)創(chuàng)建對(duì)象,那么構(gòu)造方法必須是private。

2) 工具類(lèi)不允許有public或default構(gòu)造方法。

3) 類(lèi)非static成員變量并且與子類(lèi)共享,必須是protected。

4) 類(lèi)非static成員變量并且僅在本類(lèi)使用,必須是private。

5) 類(lèi)static成員變量如果僅在本類(lèi)使用,必須是private。

6) 若是static成員變量,必須考慮是否為final。

7) 類(lèi)成員方法只供類(lèi)內(nèi)部調(diào)用,必須是private。

8) 類(lèi)成員方法只對(duì)繼承類(lèi)公開(kāi),那么限制為protected。

說(shuō)明: 任何類(lèi)、方法、參數(shù)、變量,嚴(yán)控訪(fǎng)問(wèn)范圍。過(guò)寬泛的訪(fǎng)問(wèn)范圍,不利于模塊解耦。

思考: 如果是一個(gè) private 的方法,想刪除就刪除,可是一個(gè) public 的 Service 方法,或者一個(gè) public 的成員變量,刪除一下,不得手心冒點(diǎn)汗嗎?變量像自己的小孩,盡量在自己的視 線(xiàn)內(nèi),變量作用域太大,如果無(wú)限制的到處跑,那么你會(huì)擔(dān)心的。

白話(huà):

沒(méi)什么好說(shuō)的,兩個(gè)詞,高內(nèi)聚,低耦合,功能模塊閉包,哦,是三個(gè)詞。

5 集合處理

1.【強(qiáng)制】關(guān)于 hashCode 和 equals 的處理,遵循如下規(guī)則:

1) 只要重寫(xiě)equals,就必須重寫(xiě)hashCode。

2) 因?yàn)镾et存儲(chǔ)的是不重復(fù)的對(duì)象,依據(jù)hashCode和equals進(jìn)行判斷,所以Set存儲(chǔ)的對(duì)象必須重寫(xiě)這兩個(gè)方法。

3) 如果自定義對(duì)象做為Map的鍵,那么必須重寫(xiě)hashCode和equals。

說(shuō)明: String 重寫(xiě)了 hashCode 和 equals 方法,所以我們可以非常愉快地使用 String 對(duì)象作為 key 來(lái)使用。

白話(huà):

Hash是個(gè)永恒的話(huà)題,大家可以看下times33和Murmurhash算法。

2.【強(qiáng)制】ArrayList的subList結(jié)果不可強(qiáng)轉(zhuǎn)成ArrayList,否則會(huì)拋出ClassCastException

異常: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ;

說(shuō)明: subList 返回的是 ArrayList 的內(nèi)部類(lèi) SubList,并不是 ArrayList ,而是 ArrayList 的一個(gè)視圖,對(duì)于SubList子列表的所有操作最終會(huì)反映到原列表上。

白話(huà):

這種問(wèn)題本來(lái)測(cè)試可以測(cè)試到,但是開(kāi)發(fā)永遠(yuǎn)都不要有依賴(lài)測(cè)試的想法,一切靠自己,當(dāng)然我們的測(cè)試人員都是很靠譜的。

3.【強(qiáng)制】 在 subList 場(chǎng)景中,高度注意對(duì)原集合元素個(gè)數(shù)的修改,會(huì)導(dǎo)致子列表的遍歷、增 加、刪除均產(chǎn)生ConcurrentModificationException 異常。

白話(huà):

如果一定要更改子列表,重新構(gòu)造新的ArrayList,使用

  1. public ArrayList(Collection<? extends E> c) 

4.【強(qiáng)制】使用集合轉(zhuǎn)數(shù)組的方法,必須使用集合的toArray(T[] array),傳入的是類(lèi)型完全 一樣的數(shù)組,大小就是 list.size()。

說(shuō)明: 使用 toArray 帶參方法,入?yún)⒎峙涞臄?shù)組空間不夠大時(shí),toArray 方法內(nèi)部將重新分配內(nèi)存空間,并返回新數(shù)組地址; 如果數(shù)組元素大于實(shí)際所需,下標(biāo)為[ list.size() ]的數(shù)組元素將被置為 null,其它數(shù)組元素保持原值,因此最好將方法入?yún)?shù)組大小定義與集合元素個(gè)數(shù)一致。

正例:

  1. List<String> list = new ArrayList<String>(2); list.add("guan"); 
  2. list.add("bao"); 
  3. String[] array = new String[list.size()];  
  4. array = list.toArray(array); 

反例: 直接使用 toArray 無(wú)參方法存在問(wèn)題,此方法返回值只能是 Object[]類(lèi),若強(qiáng)轉(zhuǎn)其它類(lèi)型數(shù)組將出現(xiàn) ClassCastException 錯(cuò)誤。

白話(huà):

搞不懂Java編譯器為什么不做優(yōu)化,人用邏輯能推導(dǎo)的,程序一定可以自動(dòng)實(shí)現(xiàn)。

5.【強(qiáng)制】使用工具類(lèi) Arrays.asList()把數(shù)組轉(zhuǎn)換成集合時(shí),不能使用其修改集合相關(guān)的方 法,它的 add/remove/clear 方法會(huì)拋出 UnsupportedOperationException 異常。

說(shuō)明: asList 的返回對(duì)象是一個(gè) Arrays 內(nèi)部類(lèi),并沒(méi)有實(shí)現(xiàn)集合的修改方法。Arrays.asList 體現(xiàn)的是適配器模式,只是轉(zhuǎn)換接口,后臺(tái)的數(shù)據(jù)仍是數(shù)組。

  1. String[] str = new String[] { "a""b" }; 
  2. List list = Arrays.asList(str); 

第一種情況: list.add("c"); 運(yùn)行時(shí)異常。

第二種情況: str[0] = "gujin"; 那么list.get(0)也會(huì)隨之修改。

白話(huà):

如果需要對(duì)asList返回的List做更改,可以構(gòu)造新的ArrayList,使用public ArrayList(Collection<? extends E> c)構(gòu)造器。

6.【強(qiáng)制】泛型通配符<? extends T>來(lái)接收返回的數(shù)據(jù),此寫(xiě)法的泛型集合不能使用add方 法,而<? super T>不能使用get方法,做為接口調(diào)用賦值時(shí)易出錯(cuò)。

說(shuō)明: 擴(kuò)展說(shuō)一下PECS(Producer Extends Consumer Super)原則: 1)頻繁往外讀取內(nèi)容的,適合用上界 Extends。 2)經(jīng)常往里插入的,適合用下界 Super。

白話(huà):

<? extends T>, ? 必須是T或T的子類(lèi)

集合寫(xiě)(add): 因?yàn)椴荒艽_定集合實(shí)例化時(shí)用的是T或T的子類(lèi),所以沒(méi)有辦法寫(xiě)。例如:List<? extends Number> foo = new ArrayList<Number/Integer/Double>(),你不能add Number,因?yàn)橐部赡苁荌nteger或Double的List, 同理也不能add Integer或Double,即,extends T, 不能集合add。

集合讀(get): 只能讀出T類(lèi)型的數(shù)據(jù)。

<? super T>, ? 必須是T或T的父類(lèi)

集合寫(xiě)(add): 可以add T或T的子類(lèi)。

集合讀(get): 不能確定從集合里讀出的是哪個(gè)類(lèi)型(可能是T也可能是T的父類(lèi),或者Object),所以沒(méi)有辦法使用get。例如:List<? super Integer> foo3 = new ArrayList<Integer/Number/Object>(); 只能保證get出來(lái)是Object。

下面是示例,test1和test2在編譯時(shí)都有錯(cuò)誤提示。

  1. package com.robert.javaspec; 
  2.  
  3. import java.util.LinkedList; 
  4. import java.util.List; 
  5.  
  6. /** 
  7. * Created by WangMeng on 2017-04-13. 
  8. * FIX ME 
  9. */ 
  10. public class Main { 
  11.   public static void main(String[] args) { 
  12.  
  13.   } 
  14.  
  15.   public void test1(){ 
  16.       List<? extends A> childofa=new LinkedList<>(); 
  17.       B b=new B(); 
  18.       A a=new A(); 
  19.       childofa.add(a); 
  20.       childofa.add(b); 
  21.       A ta= childofa.get(0); 
  22.   } 
  23.  
  24.   public void test2(){ 
  25.       List<? super B> superOfb = new LinkedList<>(); 
  26.       B b = new B(); 
  27.       A a = new A(); 
  28.       superOfb.add(a); 
  29.       superOfb.add(b); 
  30.       A ta = superOfb.get(0); 
  31.       B tb = superOfb.get(0); 
  32.   } 
  33.  
  34. class A { 
  35.   @Override 
  36.   public String toString() { 
  37.       return "A"
  38.   } 
  39.  
  40. class B extends A { 
  41.  
  42.   @Override 
  43.   public String toString() { 
  44.       return "B"
  45.   } 

7.【強(qiáng)制】不要在 foreach 循環(huán)里進(jìn)行元素的 remove/add 操作。remove 元素請(qǐng)使用 Iterator 方式,如果并發(fā)操作,需要對(duì) Iterator 對(duì)象加鎖。

反例:

  1. List<String> a = new ArrayList<String>();  
  2. a.add("1"); 
  3. a.add("2"); 
  4. for (String temp : a) { 
  5.     if ("1".equals(temp)) {  
  6.         a.remove(temp); 
  7.     }  

說(shuō)明: 以上代碼的執(zhí)行結(jié)果肯定會(huì)出乎大家的意料,那么試一下把“1”換成“2”,會(huì)是同樣的

結(jié)果嗎?

正例:

  1. Iterator<String> it = a.iterator();  
  2.  while (it.hasNext()) { 
  3.      String temp = it.next();  
  4.      if (刪除元素的條件) { 
  5.          it.remove(); 
  6.      } 
  7.  } 

白話(huà):

修改一定要使用Iterator。

反例中改成2,拋出ConcurrentModificationException,因?yàn)?是數(shù)組的結(jié)束邊界。

8.【強(qiáng)制】 在 JDK7 版本及以上,Comparator 要滿(mǎn)足如下三個(gè)條件,不然 Arrays.sort, Collections.sort 會(huì)報(bào) IllegalArgumentException 異常。

說(shuō)明:

  1. 1) x,y的比較結(jié)果和y,x的比較結(jié)果相反。 
  2.  2) x>y,y>z,則x>z。             
  3.  3) x=y,則x,z比較結(jié)果和y,z比較結(jié)果相同。 

反例: 下例中沒(méi)有處理相等的情況,實(shí)際使用中可能會(huì)出現(xiàn)異常:

  1. new Comparator<Student>() { 
  2.      @Override 
  3.      public int compare(Student o1, Student o2) { 
  4.          return o1.getId() > o2.getId() ? 1 : -1;  
  5.      } 
  6.  } 

白話(huà):

除非邏輯混亂,否則這些條件都能滿(mǎn)足。

9.【推薦】集合初始化時(shí),盡量指定集合初始值大小。

說(shuō)明: ArrayList盡量使用ArrayList(int initialCapacity) 初始化。

白話(huà):

預(yù)估數(shù)組大小,能夠提高程序效率,寫(xiě)代碼的時(shí)候腦袋里面要有運(yùn)行的思想。

想了解性能和容量評(píng)估,請(qǐng)參考互聯(lián)網(wǎng)性能與容量評(píng)估的方法論和典型案例。

10.【推薦】使用 entrySet 遍歷 Map 類(lèi)集合 KV,而不是 keySet 方式進(jìn)行遍歷。

說(shuō)明: keySet 其實(shí)是遍歷了 2 次,一次是轉(zhuǎn)為 Iterator 對(duì)象,另一次是從 hashMap 中取出 key 所對(duì)應(yīng)的 value。而 entrySet 只是遍歷了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,使用 Map.foreach 方法。

正例: values()返回的是 V 值集合,是一個(gè) list 集合對(duì)象;keySet()返回的是 K 值集合,是一個(gè) Set 集合對(duì)象; entrySet()返回的是 K-V 值組合集合。

白話(huà):

寫(xiě)代碼其實(shí)就是在程序員腦袋里執(zhí)行代碼的過(guò)程,直覺(jué)就是兩次肯定不如一次做完事更快。

11.【推薦】高度注意 Map 類(lèi)集合 K/V 能不能存儲(chǔ) null 值的情況,如下表格:

集合對(duì)照表

反例: 由于 HashMap 的干擾,很多人認(rèn)為 ConcurrentHashMap 是可以置入 null 值,注意存儲(chǔ) null 值時(shí)會(huì)拋出 NPE 異常。

白話(huà):

存儲(chǔ)null值場(chǎng)景不多,在防止緩存穿透的情況下,有的時(shí)候會(huì)緩存null key

12.【參考】合理利用好集合的有序性(sort)和穩(wěn)定性(order),避免集合的無(wú)序性(unsort)和 不穩(wěn)定性(unorder)帶來(lái)的負(fù)面影響。

說(shuō)明: 有序性是指遍歷的結(jié)果是按某種比較規(guī)則依次排列的。穩(wěn)定性指集合每次遍歷的元素次 序是一定的。如:ArrayList 是 order/unsort;HashMap 是 unorder/unsort;TreeSet 是 order/sort。

白話(huà):

大體上同意,但是對(duì)于HashMap理論上是無(wú)序的沒(méi)有問(wèn)題,我做了個(gè)試驗(yàn),每次輸出都是穩(wěn)定的。

數(shù)值:

  1. HashMap<IntegerInteger> map = new HashMap<IntegerInteger>(); 
  2.  
  3. map.put(3, 3); 
  4. map.put(1, 1); 
  5. map.put(2, 2); 
  6. map.put(4, 4); 
  7.  
  8. for (Entry<IntegerInteger> entry : map.entrySet()) { 
  9.     System.out.println(entry.getKey()); 

事實(shí)證明,每次輸出也是1、2、3、4,有序并且穩(wěn)定的。

字符串值:

  1. HashMap<String, String> map = new HashMap<String, String>(); 
  2.  
  3. map.put("3000""3"); 
  4. map.put("1000""1"); 
  5. map.put("2000""2"); 
  6. map.put("4000""4"); 
  7.  
  8. for (Entry<IntegerInteger> entry : map.entrySet()) { 
  9.     System.out.println(entry.getKey()); 

事實(shí)證明,每次輸出也是4000、1000、2000、3000,無(wú)序但是穩(wěn)定的。

與阿里專(zhuān)家咨詢(xún),這里HashMap不穩(wěn)定性是指rehash后輸出順序則會(huì)變化。

13.【參考】利用 Set 元素唯一的特性,可以快速對(duì)一個(gè)集合進(jìn)行去重操作,避免使用 List 的 contains 方法進(jìn)行遍歷、對(duì)比、去重操作。

白話(huà):

如果不需要精確去重,參考布隆過(guò)濾器(Bloom Filter)。

6 并發(fā)處理

1.【強(qiáng)制】獲取單例對(duì)象需要保證線(xiàn)程安全,其中的方法也要保證線(xiàn)程安全。

說(shuō)明: 資源驅(qū)動(dòng)類(lèi)、工具類(lèi)、單例工廠(chǎng)類(lèi)都需要注意。

白話(huà):

如果延遲加載實(shí)現(xiàn)的單例需要并發(fā)控制;如果初始化的時(shí)候new單例對(duì)象,本身是線(xiàn)程安全的,取得實(shí)例方法不需要同步。

2.【強(qiáng)制】創(chuàng)建線(xiàn)程或線(xiàn)程池時(shí)請(qǐng)指定有意義的線(xiàn)程名稱(chēng),方便出錯(cuò)時(shí)回溯。

正例:

  1. public class TimerTaskThread extends Thread {  
  2.      public TimerTaskThread() { 
  3.          super.setName("TimerTaskThread");  
  4.          ...  
  5.      } 
  6.  } 

白話(huà):

寫(xiě)代碼的時(shí)候就要想到查bug的時(shí)候要用到什么信息,然后決定如何命名、打印日志等。

3.【強(qiáng)制】線(xiàn)程資源必須通過(guò)線(xiàn)程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線(xiàn)程。

說(shuō)明: 使用線(xiàn)程池的好處是減少在創(chuàng)建和銷(xiāo)毀線(xiàn)程上所花的時(shí)間以及系統(tǒng)資源的開(kāi)銷(xiāo),解決資 源不足的問(wèn)題。如果不使用線(xiàn)程池,有可能造成系統(tǒng)創(chuàng)建大量同類(lèi)線(xiàn)程而導(dǎo)致消耗完內(nèi)存或者 “過(guò)度切換”的問(wèn)題。

白話(huà):

一個(gè)是使用線(xiàn)程池緩存線(xiàn)程可以提高效率,另外線(xiàn)程池幫我們做了管理線(xiàn)程的事情,提供了優(yōu)雅關(guān)機(jī)、interrupt等待IO的線(xiàn)程,飽和策略等功能。

4.【強(qiáng)制】線(xiàn)程池不允許使用 Executors 去創(chuàng)建,而是通過(guò) ThreadPoolExecutor 的方式,這樣的處理方式讓寫(xiě)的同學(xué)更加明確線(xiàn)程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。

說(shuō)明: Executors 返回的線(xiàn)程池對(duì)象的弊端如下:

1)FixedThreadPool 和 SingleThreadPool: 允許的請(qǐng)求隊(duì)列長(zhǎng)度為 Integer.MAX_VALUE,可能會(huì)堆積大量的請(qǐng)求,從而導(dǎo)致 OOM。

2)CachedThreadPool 和 ScheduledThreadPool: 允許的創(chuàng)建線(xiàn)程數(shù)量為 Integer.MAX_VALUE,可能會(huì)創(chuàng)建大量的線(xiàn)程,從而導(dǎo)致 OOM。

白話(huà):

線(xiàn)程池如果沒(méi)有限制最大數(shù)量,線(xiàn)程池?fù)伍_(kāi)的時(shí)候,由于內(nèi)存不夠或者系統(tǒng)配置的最大線(xiàn)程數(shù)超出,都會(huì)產(chǎn)生oom: unalbe to create native thread。

一個(gè)組件的核心參數(shù)最好要顯式的傳入,不要默認(rèn),就像你交給屬下一個(gè)任務(wù),任務(wù)的目標(biāo)、原則、時(shí)間點(diǎn)、邊界都要明確,不能模糊處理一樣,免得扯皮。

5.【強(qiáng)制】SimpleDateFormat 是線(xiàn)程不安全的類(lèi),一般不要定義為static變量,如果定義為

static,必須加鎖,或者使用 DateUtils 工具類(lèi)。

正例: 注意線(xiàn)程安全,使用 DateUtils。亦推薦如下處理:

  1. @Override 
  2. protected DateFormat initialValue() { 
  3.     return new SimpleDateFormat("yyyy-MM-dd"); 

說(shuō)明: 如果是 JDK8 的應(yīng)用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替 Simpledateformatter,官方給出的解釋: simple beautiful strong immutable thread-safe。

白話(huà):

記住,打死你,我也不會(huì)把SimpleDateFormat共享到類(lèi)中。

【強(qiáng)制】高并發(fā)時(shí),同步調(diào)用應(yīng)該去考量鎖的性能損耗。能用無(wú)鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖; 能鎖區(qū)塊,就不要鎖整個(gè)方法體; 能用對(duì)象鎖,就不要用類(lèi)鎖。

白話(huà):

優(yōu)先無(wú)鎖,不用鎖能解決的一定不要用鎖,即使用鎖也要控制粒度,越細(xì)越好。

7.【強(qiáng)制】對(duì)多個(gè)資源、數(shù)據(jù)庫(kù)表、對(duì)象同時(shí)加鎖時(shí),需要保持一致的加鎖順序,否則可能會(huì)造 成死鎖。

說(shuō)明: 線(xiàn)程一需要對(duì)表 A、B、C 依次全部加鎖后才可以進(jìn)行更新操作,那么線(xiàn)程二的加鎖順序也必須是 A、B、C,否則可能出現(xiàn)死鎖。

白話(huà):

解決死鎖的方法:按順序鎖資源、超時(shí)、優(yōu)先級(jí)、死鎖檢測(cè)等。

可參考哲學(xué)家進(jìn)餐問(wèn)題學(xué)習(xí)更深入的并發(fā)機(jī)制。

8.【強(qiáng)制】并發(fā)修改同一記錄時(shí),避免更新丟失,需要加鎖。要么在應(yīng)用層加鎖,要么在緩存加 鎖,要么在數(shù)據(jù)庫(kù)層使用樂(lè)觀(guān)鎖,使用 version 作為更新依據(jù)。

說(shuō)明: 如果每次訪(fǎng)問(wèn)沖突概率小于 20%,推薦使用樂(lè)觀(guān)鎖,否則使用悲觀(guān)鎖。樂(lè)觀(guān)鎖的重試次 數(shù)不得小于 3 次。

白話(huà):

狀態(tài)流轉(zhuǎn)、維護(hù)可用余額等最好直接利用數(shù)據(jù)庫(kù)的行級(jí)鎖,不需要顯式的加鎖。

9.【強(qiáng)制】多線(xiàn)程并行處理定時(shí)任務(wù)時(shí),Timer 運(yùn)行多個(gè) TimerTask 時(shí),只要其中之一沒(méi)有捕獲拋出的異常,其它任務(wù)便會(huì)自動(dòng)終止運(yùn)行,使用 ScheduledExecutorService 則沒(méi)有這個(gè)問(wèn)題。

白話(huà):

線(xiàn)程執(zhí)行體、任務(wù)最上層等一定要抓住Throwable并進(jìn)行相應(yīng)的處理,否則會(huì)使線(xiàn)程終止。

10.【推薦】使用 CountDownLatch 進(jìn)行異步轉(zhuǎn)同步操作,每個(gè)線(xiàn)程退出前必須調(diào)用 countDown 方法,線(xiàn)程執(zhí)行代碼注意 catch 異常,確保 countDown 方法可以執(zhí)行,避免主線(xiàn)程無(wú)法執(zhí)行至 await 方法,直到超時(shí)才返回結(jié)果。

說(shuō)明: 注意,子線(xiàn)程拋出異常堆棧,不能在主線(xiàn)程 try-catch 到。

白話(huà):

請(qǐng)?jiān)趖ry...finally語(yǔ)句里執(zhí)行countDown方法,與關(guān)閉資源類(lèi)似。

11.【推薦】避免 Random 實(shí)例被多線(xiàn)程使用,雖然共享該實(shí)例是線(xiàn)程安全的,但會(huì)因競(jìng)爭(zhēng)同一 seed 導(dǎo)致的性能下降。

說(shuō)明: Random 實(shí)例包括 java.util.Random 的實(shí)例或者 Math.random()實(shí)例。

正例: 在 JDK7 之后,可以直接使用 API ThreadLocalRandom,在 JDK7 之前,可以做到每個(gè)線(xiàn)程一個(gè)實(shí)例。

白話(huà):

可以把Random放在ThreadLocal里,只在本線(xiàn)程中使用。

12.【推薦】在并發(fā)場(chǎng)景下,通過(guò)雙重檢查鎖(double-checked locking)實(shí)現(xiàn)延遲初始化的優(yōu) 化問(wèn)題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問(wèn) 題解決方案中較為簡(jiǎn)單一種(適用于 JDK5 及以上版本),將目標(biāo)屬性聲明為 volatile 型。

反例:

  1. class Foo { 
  2.     private Helper helper = null;  
  3.     public Helper getHelper() { 
  4.         if (helper == null)  
  5.             synchronized(this) {  
  6.                 if (helper == null
  7.                     helper = new Helper(); 
  8.             } 
  9.             return helper;  
  10.     } 
  11.     // other functions and members... 

白話(huà):

網(wǎng)上對(duì)雙檢鎖有N多討論,這里很負(fù)責(zé)任的告訴大家,只要不是特別老的JDK版本(1.4以下),雙檢鎖是沒(méi)問(wèn)題的。

13.【參考】volatile 解決多線(xiàn)程內(nèi)存不可見(jiàn)問(wèn)題。對(duì)于一寫(xiě)多讀,是可以解決變量同步問(wèn)題, 但是如果多寫(xiě),同樣無(wú)法解決線(xiàn)程安全問(wèn)題。如果是 count++操作,使用如下類(lèi)實(shí)現(xiàn): AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推薦使用 LongAdder 對(duì)象,比 AtomicLong 性能更好(減少樂(lè)觀(guān)鎖的重試次數(shù))。

白話(huà):

volatile只有內(nèi)存可見(jiàn)性語(yǔ)義,synchronized有互斥語(yǔ)義,一寫(xiě)多讀使用volatile就可以,多寫(xiě)就必須使用synchronized,fetch-mod-get也必須使用synchronized。

14.【參考】 HashMap 在容量不夠進(jìn)行 resize 時(shí)由于高并發(fā)可能出現(xiàn)死鏈,導(dǎo)致 CPU 飆升,在開(kāi)發(fā)過(guò)程中注意規(guī)避此風(fēng)險(xiǎn)。

白話(huà):

開(kāi)發(fā)程序的時(shí)候要預(yù)估使用量,根據(jù)使用量來(lái)設(shè)置初始值。

resize需要重建hash表,嚴(yán)重影響性能,會(huì)讓程序產(chǎn)生長(zhǎng)尾的響應(yīng)時(shí)間。

15.【參考】ThreadLocal 無(wú)法解決共享對(duì)象的更新問(wèn)題,ThreadLocal 對(duì)象建議使用 static 修飾。這個(gè)變量是針對(duì)一個(gè)線(xiàn)程內(nèi)所有操作共有的,所以設(shè)置為靜態(tài)變量,所有此類(lèi)實(shí)例共享此靜態(tài)變量 ,也就是說(shuō)在類(lèi)第一次被使用時(shí)裝載,只分配一塊存儲(chǔ)空間,所有此類(lèi)的對(duì)象(只 要是這個(gè)線(xiàn)程內(nèi)定義的)都可以操控這個(gè)變量。

白話(huà):

ThreadLocal實(shí)際上是一個(gè)從線(xiàn)程ID到變量的Map,每次取得ThreadLocal變量,實(shí)際上是先取得當(dāng)前線(xiàn)程ID,再用當(dāng)前線(xiàn)程ID取得關(guān)聯(lián)的變量。

ThreadLocal使用了WeakHashMap,在key被回收的時(shí)候,value也被回收了,不用擔(dān)心內(nèi)存泄露。

7 控制語(yǔ)句

1.【強(qiáng)制】在一個(gè) switch 塊內(nèi),每個(gè) case 要么通過(guò) break/return 等來(lái)終止,要么注釋說(shuō)明程序?qū)⒗^續(xù)執(zhí)行到哪一個(gè) case 為止;在一個(gè) switch 塊內(nèi),都必須包含一個(gè) default 語(yǔ)句并且放在最后,即使它什么代碼也沒(méi)有。

白話(huà):

最好每個(gè)case都用break結(jié)束,不要組合幾個(gè)分支到一個(gè)邏輯,太不直觀(guān)。

2.【強(qiáng)制】在 if/else/for/while/do 語(yǔ)句中必須使用大括號(hào),即使只有一行代碼,避免使用 下面的形式:if (condition) statements;

白話(huà):

這條有歧義,個(gè)人認(rèn)為有的時(shí)候就一行語(yǔ)句不加也可以。

3.【推薦】推薦盡量少用 else, if-else 的方式可以改寫(xiě)成:

  1. if (condition) { ... 
  2.     return obj;  
  3. // 接著寫(xiě) else 的業(yè)務(wù)邏輯代碼; 

說(shuō)明: 如果非得使用if()...else if()...else...方式表達(dá)邏輯,【強(qiáng)制】請(qǐng)勿超過(guò)3層, 超過(guò)請(qǐng)使用狀態(tài)設(shè)計(jì)模式。

正例: 邏輯上超過(guò) 3 層的 if-else 代碼可以使用衛(wèi)語(yǔ)句,或者狀態(tài)模式來(lái)實(shí)現(xiàn)。

白話(huà):

朋友說(shuō)超過(guò)三層考慮狀態(tài)設(shè)計(jì)模式也不完全正確,大概可以理解為多層的邏輯嵌套不是好的代碼風(fēng)格,需要使用對(duì)應(yīng)的重構(gòu)方法做出優(yōu)化,而每種壞味都有對(duì)應(yīng)的優(yōu)化方法和步驟,以及優(yōu)缺點(diǎn)限制條件。

寫(xiě)程序一定要遵守紅花綠葉原則,主邏輯放在主方法中,這是紅花,子邏輯封裝成小方法調(diào)用,這是綠葉,不要把不同層次的邏輯寫(xiě)在一個(gè)大方法體里,很難理解,就像綠葉把紅花擋住了,誰(shuí)還能看到。舉例說(shuō)明:

  1. public void handleProcess() { 
  2.   // 骨架邏輯 
  3.   validate(); 
  4.   doProcess(); 
  5.   declareResource(); 

普及一下,如下類(lèi)似排比句的代碼就是衛(wèi)語(yǔ)句,以前每天都這么寫(xiě)但是還真是剛剛知道這叫衛(wèi)語(yǔ)句:)

  1. public double getPayAmount() {   
  2.  if (isDead()) return deadPayAmount();   
  3.  if (isSeparated()) return separatedPayAmount();   
  4.  if (isRetired()) return retiredPayAmount();   
  5.  return normalPayAmount();   
  6.  

不提倡的寫(xiě)法:

  1. public double getPayAmount() {   
  2.  if (isDead())  
  3.      return deadPayAmount();   
  4.  else if (isSeparated())  
  5.      return separatedPayAmount();   
  6.  else if (isRetired()) 
  7.      return retiredPayAmount();   
  8.  else  
  9.      return normalPayAmount();   

4.【推薦】除常用方法(如 getXxx/isXxx)等外,不要在條件判斷中執(zhí)行其它復(fù)雜的語(yǔ)句,將復(fù) 雜邏輯判斷的結(jié)果賦值給一個(gè)有意義的布爾變量名,以提高可讀性。

說(shuō)明: 很多 if 語(yǔ)句內(nèi)的邏輯相當(dāng)復(fù)雜,閱讀者需要分析條件表達(dá)式的最終結(jié)果,才能明確什么 樣的條件執(zhí)行什么樣的語(yǔ)句,那么,如果閱讀者分析邏輯表達(dá)式錯(cuò)誤呢?

正例:

//偽代碼如下

  1. //偽代碼如下 
  2.  boolean existed = (file.open(fileName, "w") != null) && (...) || (...);  
  3.  if (existed) { 
  4.      ...  
  5.  } 

反例:

  1. if ((file.open(fileName, "w") != null) && (...) || (...)) { ...} 

白話(huà):

這個(gè)反例真的經(jīng)常見(jiàn)到,寫(xiě)這個(gè)代碼的人自己不覺(jué)得這樣很難看嗎?

5.【推薦】循環(huán)體中的語(yǔ)句要考量性能,以下操作盡量移至循環(huán)體外處理,如定義對(duì)象、變量、

獲取數(shù)據(jù)庫(kù)連接,進(jìn)行不必要的 try-catch 操作(這個(gè) try-catch 是否可以移至循環(huán)體外)。

白話(huà):

切記,循環(huán)體內(nèi)盡量不要獲取資源、不要處理異常。

6.【推薦】接口入?yún)⒈Wo(hù),這種場(chǎng)景常見(jiàn)的是用于做批量操作的接口。

白話(huà):

用白話(huà)說(shuō),就是控制批量參數(shù)的數(shù)量,一次不能太多,否則內(nèi)存溢出。

7.【參考】方法中需要進(jìn)行參數(shù)校驗(yàn)的場(chǎng)景:

1) 調(diào)用頻次低的方法。

2) 執(zhí)行時(shí)間開(kāi)銷(xiāo)很大的方法,參數(shù)校驗(yàn)時(shí)間幾乎可以忽略不計(jì),但如果因?yàn)閰?shù)錯(cuò)誤導(dǎo)致

中間執(zhí)行回退,或者錯(cuò)誤,那得不償失。

3) 需要極高穩(wěn)定性和可用性的方法。

4) 對(duì)外提供的開(kāi)放接口,不管是RPC/API/HTTP接口。

5) 敏感權(quán)限入口。

白話(huà):

在這個(gè)框框內(nèi),根據(jù)業(yè)務(wù)適當(dāng)調(diào)整是可以的。

8.【參考】方法中不需要參數(shù)校驗(yàn)的場(chǎng)景:

1) 極有可能被循環(huán)調(diào)用的方法,不建議對(duì)參數(shù)進(jìn)行校驗(yàn)。但在方法說(shuō)明里必須注明外部參

數(shù)檢查要求。

2) 底層的方法調(diào)用頻度都比較高,一般不校驗(yàn)。畢竟是像純凈水過(guò)濾的最后一道,參數(shù)錯(cuò)

誤不太可能到底層才會(huì)暴露問(wèn)題。一般 DAO 層與 Service 層都在同一個(gè)應(yīng)用中,部署在同一 臺(tái)服務(wù)器中,所以 DAO 的參數(shù)校驗(yàn),可以省略。

3) 被聲明成private只會(huì)被自己代碼所調(diào)用的方法,如果能夠確定調(diào)用方法的代碼傳入?yún)?shù)已經(jīng)做過(guò)檢查或者肯定不會(huì)有問(wèn)題,此時(shí)可以不校驗(yàn)參數(shù)。

白話(huà):

在這個(gè)框框呢,根據(jù)業(yè)務(wù)適當(dāng)調(diào)整是可以的。

8 注釋規(guī)約

1.【強(qiáng)制】類(lèi)、類(lèi)屬性、類(lèi)方法的注釋必須使用 Javadoc 規(guī)范,使用/*內(nèi)容/格式,不得使用 //xxx 方式。

說(shuō)明:在 IDE 編輯窗口中,Javadoc 方式會(huì)提示相關(guān)注釋?zhuān)?Javadoc 可以正確輸出相應(yīng)注 釋; 在 IDE 中,工程調(diào)用方法時(shí),不進(jìn)入方法即可懸浮提示方法、參數(shù)、返回值的意義,提高閱讀效率。

白話(huà):

開(kāi)發(fā)環(huán)境編輯器可保證這一條。

2.【強(qiáng)制】所有的抽象方法(包括接口中的方法)必須要用 Javadoc 注釋、除了返回值、參數(shù)、 異常說(shuō)明外,還必須指出該方法做什么事情,實(shí)現(xiàn)什么功能。

說(shuō)明: 對(duì)子類(lèi)的實(shí)現(xiàn)要求,或者調(diào)用注意事項(xiàng),請(qǐng)一并說(shuō)明。

白話(huà):

文檔還要涵蓋校驗(yàn),線(xiàn)程安全等,重要方法要給出調(diào)用示例。

3.【強(qiáng)制】所有的類(lèi)都必須添加創(chuàng)建者信息。

白話(huà):

做好事要留名,要不誰(shuí)知道是你干的,方法可參考雷鋒,雷鋒每次做好事不留名,每次時(shí)間地點(diǎn)都被記下來(lái)了。

4.【強(qiáng)制】方法內(nèi)部單行注釋?zhuān)诒蛔⑨屨Z(yǔ)句上方另起一行,使用//注釋。方法內(nèi)部多行注釋

使用/ /注釋?zhuān)⒁馀c代碼對(duì)齊。

白話(huà):

關(guān)鍵的地方做注釋?zhuān)膊灰啵a最好是自文檔化,看代碼就能讓人理解,這是境界。

5.【強(qiáng)制】所有的枚舉類(lèi)型字段必須要有注釋?zhuān)f(shuō)明每個(gè)數(shù)據(jù)項(xiàng)的用途。

白話(huà):

枚舉是個(gè)非常重要的類(lèi)型,業(yè)務(wù)代碼中大量存在,注釋是必須的。

6.【推薦】與其“半吊子”英文來(lái)注釋?zhuān)蝗缬弥形淖⑨尠褑?wèn)題說(shuō)清楚。專(zhuān)有名詞與關(guān)鍵字保持英文原文即可。

反例:“TCP 連接超時(shí)”解釋成“傳輸控制協(xié)議連接超時(shí)”,理解反而費(fèi)腦筋。

白話(huà):

還是那句話(huà),脫了褲子放屁還是累,又沒(méi)人感激你翻譯了。

7.【推薦】代碼修改的同時(shí),注釋也要進(jìn)行相應(yīng)的修改,尤其是參數(shù)、返回值、異常、核心邏輯 等的修改。

說(shuō)明: 代碼與注釋更新不同步,就像路網(wǎng)與導(dǎo)航軟件更新不同步一樣,如果導(dǎo)航軟件嚴(yán)重滯后, 就失去了導(dǎo)航的意義。

白話(huà):

很多線(xiàn)上發(fā)生的應(yīng)急事故都是技術(shù)債積累到一定程度,量變導(dǎo)致質(zhì)變,才發(fā)生了重大的災(zāi)難型事件。因此,代碼要與時(shí)俱進(jìn),代碼改變,注釋要改變,文檔要改變,要周知相關(guān)方。問(wèn)題是很難實(shí)現(xiàn),因?yàn)檫@是反人性的,做了這個(gè)事情對(duì)個(gè)人沒(méi)有什么利益,有些人就不做,只有對(duì)代碼有潔癖的人才會(huì)這么做。

8.【參考】注釋掉的代碼盡量要配合說(shuō)明,而不是簡(jiǎn)單的注釋掉。

說(shuō)明: 代碼被注釋掉有兩種可能性:1)后續(xù)會(huì)恢復(fù)此段代碼邏輯。2)永久不用。前者如果沒(méi) 有備注信息,難以知曉注釋動(dòng)機(jī)。后者建議直接刪掉(代碼倉(cāng)庫(kù)保存了歷史代碼)。

白話(huà):

盡量不要留下注釋掉的代碼,曾經(jīng)發(fā)生合并代碼的時(shí)候不小心把注釋的代碼打開(kāi)了,于是,悲劇了,源代碼管理工具,例如:git、svn都有歷史記錄可查詢(xún)的,沒(méi)必要注釋代碼并留在源碼中。

9.【參考】對(duì)于注釋的要求: 第一、能夠準(zhǔn)確反應(yīng)設(shè)計(jì)思想和代碼邏輯;第二、能夠描述業(yè)務(wù)含義,使別的程序員能夠迅速了解到代碼背后的信息。完全沒(méi)有注釋的大段代碼對(duì)于閱讀者形同天書(shū),注釋是給自己看的,即使隔很長(zhǎng)時(shí)間,也能清晰理解當(dāng)時(shí)的思路;注釋也是給繼任者看的,使其能夠快速接替自己的工作。

白話(huà):

還是那句話(huà),這個(gè)是反人性的,注釋好了又有什么用,可以讓更多的人掌握我的代碼嗎?職場(chǎng)上似乎不是一個(gè)好事 :),不過(guò)專(zhuān)業(yè)的程序員更愿意碼好每一段代碼是毋庸置疑的。

10.【參考】好的命名、代碼結(jié)構(gòu)是自解釋的,注釋力求精簡(jiǎn)準(zhǔn)確、表達(dá)到位。避免出現(xiàn)注釋的

一個(gè)極端: 過(guò)多過(guò)濫的注釋?zhuān)a的邏輯一旦修改,修改注釋是相當(dāng)大的負(fù)擔(dān)。

反例:

  1. // put elephant into fridge 
  2.    put(elephant, fridge); 

方法名put,加上兩個(gè)有意義的變量名 elephant 和 fridge,已經(jīng)說(shuō)明了這是在干什么,語(yǔ)

義清晰的代碼不需要額外的注釋。

白話(huà):

寫(xiě)注釋的最高境界是不用注釋?zhuān)a一目了然,看了就懂。

11.【參考】特殊注釋標(biāo)記,請(qǐng)注明標(biāo)記人與標(biāo)記時(shí)間。注意及時(shí)處理這些標(biāo)記,通過(guò)標(biāo)記掃描, 經(jīng)常清理此類(lèi)標(biāo)記。線(xiàn)上故障有時(shí)候就是來(lái)源于這些標(biāo)記處的代碼。

1) 待辦事宜(TODO):( 標(biāo)記人,標(biāo)記時(shí)間,[預(yù)計(jì)處理時(shí)間]) 表示需要實(shí)現(xiàn),但目前還未實(shí)現(xiàn)的功能。這實(shí)際上是一個(gè) Javadoc 的標(biāo)簽,目前的 Javadoc

還沒(méi)有實(shí)現(xiàn),但已經(jīng)被廣泛使用。只能應(yīng)用于類(lèi),接口和方法(因?yàn)樗且粋€(gè) Javadoc 標(biāo)簽)。

2) 錯(cuò)誤,不能工作(FIXME):(標(biāo)記人,標(biāo)記時(shí)間,[預(yù)計(jì)處理時(shí)間])

在注釋中用 FIXME 標(biāo)記某代碼是錯(cuò)誤的,而且不能工作,需要及時(shí)糾正的情況。

白話(huà):

我很負(fù)責(zé)人的說(shuō),經(jīng)常使用TODO和FIXME的程序員都是好程序員。

9 其他

1.【強(qiáng)制】在使用正則表達(dá)式時(shí),利用好其預(yù)編譯功能,可以有效加快正則匹配速度。

說(shuō)明: 不要在方法體內(nèi)定義:Pattern pattern = Pattern.compile(規(guī)則);

白話(huà):

Pattern和Random都是線(xiàn)程安全的,而MessageDigest, SimpleDateFormat不是線(xiàn)程安全的。

2.【強(qiáng)制】velocity 調(diào)用 POJO 類(lèi)的屬性時(shí),建議直接使用屬性名取值即可,模板引擎會(huì)自動(dòng)按規(guī)范調(diào)用 POJO 的 getXxx(),如果是 boolean 基本數(shù)據(jù)類(lèi)型變量(boolean 命名不需要加 is 前綴),會(huì)自動(dòng)調(diào)用 isXxx()方法。

說(shuō)明: 注意如果是 Boolean 包裝類(lèi)對(duì)象,優(yōu)先調(diào)用 getXxx()的方法。

白話(huà):

屬性前不要加is,這類(lèi)變量最好用形容詞表達(dá)。

3.【強(qiáng)制】后臺(tái)輸送給頁(yè)面的變量必須加$!{var}——中間的感嘆號(hào)。 說(shuō)明:如果 var=null 或者不存在,那么${var}會(huì)直接顯示在頁(yè)面上。

白話(huà):

這里說(shuō)的是velocity,${var}在var為null的時(shí)候,直接把代碼${var}打印在界面上,太不專(zhuān)業(yè),也有安全問(wèn)題,必須使用${var}代替。

4.【強(qiáng)制】注意 Math.random() 這個(gè)方法返回是 double 類(lèi)型,注意取值的范圍 0≤x<1(能夠取到零值,注意除零異常),如果想獲取整數(shù)類(lèi)型的隨機(jī)數(shù),不要將 x 放大 10 的若干倍然后 取整,直接使用 Random 對(duì)象的 nextInt 或者 nextLong 方法。

白話(huà):

毋庸置疑,用專(zhuān)業(yè)的工具干專(zhuān)業(yè)的事兒。

5.【強(qiáng)制】獲取當(dāng)前毫秒數(shù) System.currentTimeMillis(); 而不是 new Date().getTime();

說(shuō)明: 如果想獲取更加精確的納秒級(jí)時(shí)間值,用 System.nanoTime()。在 JDK8 中,針對(duì)統(tǒng)計(jì)時(shí)間等場(chǎng)景,推薦使用 Instant 類(lèi)。

白話(huà):

Date內(nèi)部也是使用System.currentTimeMillis實(shí)現(xiàn)的,使用Date還得多構(gòu)造一個(gè)對(duì)象,在量級(jí)很大的時(shí)候會(huì)有一些性能損耗。

6.【推薦】盡量不要在 velocity 模板中加入變量聲明、邏輯運(yùn)算符,更不要在模板中加入任何復(fù)雜的邏輯。

白話(huà):

MVC!視圖的職責(zé)是展示,不要搶人家模型和控制器的活!

7.【推薦】任何數(shù)據(jù)結(jié)構(gòu)的構(gòu)造或初始化,都應(yīng)指定大小,避免數(shù)據(jù)結(jié)構(gòu)無(wú)限增長(zhǎng)吃光內(nèi)存。

白話(huà):

尤其是集合、批量參數(shù)、數(shù)據(jù)庫(kù)表都要有最大數(shù)量的限制,否則就為OOM埋下隱患。

8.【推薦】對(duì)于“明確停止使用的代碼和配置”,如方法、變量、類(lèi)、配置文件、動(dòng)態(tài)配置屬性

等要堅(jiān)決從程序中清理出去,避免造成過(guò)多垃圾。

白話(huà):

要保持衛(wèi)生,屋子太亂了也影響生活的情趣,代碼也要定期捯飭一下。

10 總結(jié)

在總結(jié)的過(guò)程中,又對(duì)阿里的每一條規(guī)范進(jìn)行了深入的思考,并與阿里人員以及業(yè)內(nèi)Java專(zhuān)家進(jìn)行了討論,對(duì)難于理解的條目進(jìn)行了說(shuō)明,并進(jìn)行了適當(dāng)?shù)男畔⒀a(bǔ)充,用白話(huà)的形式表達(dá)出來(lái),能夠感覺(jué)到阿里開(kāi)發(fā)規(guī)范的每一條都是線(xiàn)上大規(guī)模服務(wù)化系統(tǒng)中的踩坑指南,這和筆者正在做的事情一樣,線(xiàn)上出了問(wèn)題會(huì)復(fù)盤(pán),復(fù)盤(pán)后找到問(wèn)題的原因,總結(jié)歸納,并在研發(fā)流程規(guī)范中體現(xiàn)解決方案和避免措施,然后推廣給開(kāi)發(fā)人員,讓大家在今后的開(kāi)發(fā)中輕而易舉的避免犯同樣的錯(cuò)誤,從而避免發(fā)生同樣的生產(chǎn)事故,關(guān)于生產(chǎn)事故,向大家推薦電影《深海浩劫》。

本文為五篇系列文章的第一篇-編程規(guī)約,后續(xù)會(huì)陸續(xù)發(fā)布其余的文章,內(nèi)容包括:異常日志、MySQL規(guī)約、工程規(guī)約、安全規(guī)約等。

【本文為51CTO專(zhuān)欄作者“李艷鵬”的原創(chuàng)稿件,轉(zhuǎn)載可通過(guò)作者簡(jiǎn)書(shū)號(hào)(李艷鵬)或51CTO專(zhuān)欄獲取聯(lián)系】

戳這里,看該作者更多好文

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專(zhuān)欄
相關(guān)推薦

2025-04-17 08:47:23

2020-12-18 10:55:51

阿里巴巴Redis數(shù)據(jù)庫(kù)

2010-06-28 10:43:47

2013-08-22 09:41:52

阿里巴巴去IOE王堅(jiān)

2020-09-14 09:47:56

Java開(kāi)發(fā)類(lèi)型

2017-02-23 16:34:46

Java阿里巴巴

2009-02-27 10:46:32

DBA筆試題阿里巴巴

2023-03-29 09:42:32

2019-08-15 10:25:02

代碼開(kāi)發(fā)工具

2013-08-22 09:36:45

阿里巴巴王堅(jiān)阿里云

2020-04-28 10:54:13

運(yùn)算符開(kāi)發(fā)Java

2009-06-30 13:28:54

阿里巴巴旺旺

2014-12-31 10:48:40

阿里巴巴馬云

2009-06-22 09:38:49

阿里巴巴辦公環(huán)境

2010-08-25 14:33:59

抄底

2015-04-23 15:30:08

2015-06-12 10:12:30

2015-05-12 15:09:01

阿里巴巴公有云IaaS

2018-10-16 15:34:17

阿里巴巴Apache Flin大數(shù)據(jù)

2022-08-22 08:07:45

DruidMySQL密碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

国产欧美在线视频| 久久精品国产欧美激情| 成人免费无码av| 日本亚洲精品| 不卡的av电影在线观看| 国产精品激情av电影在线观看| 日韩亚洲欧美中文字幕| av成人资源| 色悠久久久久综合欧美99| 黄频视频在线观看| 青青草超碰在线| 国产精品一卡二| 国产精品99久久久久久久久久久久| 中日韩一级黄色片| 亚洲激情播播| 日韩精品一区二区三区四区| 成人在线观看a| 国产99re66在线视频| 中文av一区特黄| 久99久视频| 午夜精品一区二区三| 久热成人在线视频| 欧美在线视频网站| 久久久久久久国产精品毛片| 色综合咪咪久久网| 亚洲欧美成人在线| 插我舔内射18免费视频| 国产精品免费精品自在线观看| 91国在线观看| 777久久久精品一区二区三区| 在线视频观看国产| 中文字幕一区二区三区不卡在线 | 在线观看的毛片| 成年人视频免费在线播放| 亚洲欧洲三级电影| 色一情一乱一伦一区二区三区丨| 少妇av一区二区| 国产suv精品一区二区6| 成人精品视频在线| 91禁在线观看| 美女在线视频一区| 国产精品色视频| 欧美成人精品网站| 久久久精品网| 国产成人亚洲综合91精品| 亚洲av中文无码乱人伦在线视色| 一区二区三区国产盗摄| 7777kkkk成人观看| 精品国产乱码一区二区| 亚洲全部视频| 77777亚洲午夜久久多人| 国产精品一区二区6| 亚洲综合另类| 日韩av片电影专区| 做爰无遮挡三级| 美女视频网站黄色亚洲| 国产精品入口福利| 中文字幕永久免费视频| 久久99国产精品麻豆| 成人天堂噜噜噜| av 一区二区三区| 国产成人在线电影| 国产精品一区视频| 四虎成人免费在线| 国产亚洲一二三区| 日韩欧美亚洲日产国| 色网站免费在线观看| 18欧美亚洲精品| 久久这里只有精品8| 888av在线视频| 色综合天天狠狠| 亚洲国产高清av| 精品视频一二| 亚洲精品av在线播放| 成人片黄网站色大片免费毛片| 国产一区二区三区电影在线观看| 色哟哟网站入口亚洲精品| 顶臀精品视频www| 亚洲高清久久| 国产精品成人va在线观看| 一区二区国产欧美| 成人网在线免费视频| 免费成人看片网址| 求av网址在线观看| 亚洲国产一区二区三区| 北条麻妃在线视频| 激情五月综合婷婷| 亚洲精品美女久久久久| 91精品国产自产| 亚洲成av人片乱码色午夜| 国内免费精品永久在线视频| 久久久999久久久| 国产精品资源网| 欧美二级三级| 91精品久久久久久粉嫩| 欧美性生交大片免网| 五月天丁香花婷婷| 午夜精品影视国产一区在线麻豆| 中文字幕日韩电影| 国产精品第九页| 老司机一区二区| 国模精品一区二区三区| 麻豆系列在线观看| 欧美日韩在线看| 亚洲三级在线视频| 精品国产乱码久久久| 欧美精品xxx| 国产精品区在线观看| 26uuu成人网一区二区三区| 熟妇熟女乱妇乱女网站| 成人午夜精品| 亚洲成人aaa| 中文字幕在线有码| 日韩成人午夜电影| 久久亚洲午夜电影| 国产后进白嫩翘臀在线观看视频| 欧美日韩在线不卡| www.超碰97| 亚洲网站啪啪| 999久久久| 黄色av免费在线| 欧美日本不卡视频| 亚洲激情视频小说| 一本综合久久| 动漫精品视频| 1区2区3区在线视频| 欧美欧美午夜aⅴ在线观看| 亚洲熟妇一区二区三区| 亚洲电影av| 国产精品久久亚洲| 午夜影院免费在线| 56国语精品自产拍在线观看| 懂色av粉嫩av浪潮av| 久久精品首页| 鲁鲁狠狠狠7777一区二区| а√在线中文在线新版| 精品毛片乱码1区2区3区| 黑人巨大精品一区二区在线| 国产精品资源在线| 4444在线观看| 一区二区三区视频免费视频观看网站| 久热精品视频在线观看| 91av久久久| 成人欧美一区二区三区黑人麻豆| 亚洲天堂网2018| 99久久99热这里只有精品| 91精品国产综合久久久久久蜜臀 | 精品视频国产| 国产成人免费av| 精品欧美不卡一区二区在线观看| 色综合天天做天天爱| 免费在线观看成年人视频| 亚洲免费综合| 欧洲一区二区在线| 精品国产黄a∨片高清在线| 日韩在线观看免费av| 国产精品系列视频| 一区二区三区在线观看国产| 性高潮免费视频| 9色精品在线| 欧美国产二区| 色诱色偷偷久久综合| 久久中文字幕一区| 亚洲精品一区二区口爆| 好吊成人免视频| 中文字幕在线观看免费高清| 美女脱光内衣内裤视频久久影院| 中文字幕日韩一区二区三区不卡| 亚洲国产精品免费视频| 欧美黑人性猛交| 四虎影视精品成人| 欧美老女人第四色| 九九热只有精品| 97久久超碰国产精品电影| 免费看a级黄色片| 五月开心六月丁香综合色啪 | 日韩av不卡电影| 最近高清中文在线字幕在线观看| 欧美一区二区三区人| 中文字幕第28页| 国产日韩欧美精品电影三级在线 | 中文字幕 国产精品| 1000部国产精品成人观看| 69亚洲乱人伦| 奇米影视一区二区三区| 日本a在线天堂| 国产va免费精品观看精品视频| 91精品久久久久久综合乱菊 | 菠萝菠萝蜜网站| 蜜臀av性久久久久蜜臀aⅴ流畅| 日韩中文在线字幕| 羞羞色国产精品网站| 91久久在线观看| 在线女人免费视频| 欧美高清在线播放| 国产精品ⅴa有声小说| 欧美不卡视频一区| 特级西西444www高清大视频| 亚洲成人动漫精品| 国产成人免费在线观看视频| 99久久综合色| 婷婷激情小说网| 日韩国产欧美在线播放| 亚洲色成人www永久在线观看 | 在线观看国产精品入口男同| 亚洲一区二区三区中文字幕| 天天摸日日摸狠狠添| a亚洲天堂av| 久久久久久久久久毛片| 人妖欧美一区二区| 女人和拘做爰正片视频| 欧美日韩网址| 久久免费看毛片| 欧美日韩在线二区| 久久精品美女| 成人av动漫| 91色在线视频| 久久精品xxxxx| 国产精品777| 在线免费看h| 国产91成人在在线播放| 男女视频在线| 操日韩av在线电影| 日本免费在线观看| 国产一区二区三区日韩欧美| 蜜桃视频在线观看网站| 日韩av网站在线| 三级在线观看网站| 精品第一国产综合精品aⅴ| 国产女同91疯狂高潮互磨| 欧美无砖砖区免费| 奴色虐av一区二区三区| 91久久精品午夜一区二区| 久久国产视频播放| 欧美日韩在线视频观看| 日本在线播放视频| 大桥未久av一区二区三区| 精品国产免费观看| 色综合天天综合网国产成人综合天 | 日韩免费av在线| 在线免费三级电影网站| 青青草原成人在线视频| 中文字幕不卡三区视频| 秋霞成人午夜鲁丝一区二区三区| 少妇视频在线观看| 热久久视久久精品18亚洲精品| 蜜桃麻豆影像在线观看| 1769国产精品| 日韩成人影音| 国产激情久久久久| 国产精品99| 成人情趣片在线观看免费| 玖玖玖电影综合影院| 99一区二区三区| 伦理一区二区| 欧美日韩大片一区二区三区| 欧美伦理在线视频| 在线视频不卡一区二区三区| 午夜精品剧场| 日韩少妇内射免费播放| 天堂午夜影视日韩欧美一区二区| 爱情岛论坛成人| 韩国v欧美v亚洲v日本v| 精品人妻无码中文字幕18禁| 99这里只有久久精品视频| 好吊日免费视频| 国产精品免费人成网站| 欧美黑人猛猛猛| 疯狂做受xxxx高潮欧美日本| 日韩精选在线观看| 日韩亚洲欧美成人一区| 天堂在线资源8| 国产一区二区三区欧美| 中文在线免费| 欧美做爰性生交视频| 国产成人a视频高清在线观看| 444亚洲人体| 夜色77av精品影院| 国产精品h视频| 午夜亚洲性色福利视频| 国产永久免费网站| 99久久精品免费| 一本一本久久a久久| 亚洲国产婷婷综合在线精品| 免费精品一区二区| 日韩欧美三级在线| 久草在线免费福利资源| 欧美成人精品在线视频| 欧美男体视频| 99re视频在线| 成人av二区| 国产免费黄色小视频| 久久99在线观看| 青青草视频播放| 一区二区三区在线视频观看| 91黑人精品一区二区三区| 日韩欧美一级特黄在线播放| 成人在线免费电影| 91精品国产色综合久久不卡98| 国产精品天堂蜜av在线播放| 精品视频第一区| 中文字幕日韩一区二区不卡| 欧美牲交a欧美牲交aⅴ免费真| 国产伦精品一区二区三区免费迷| 亚洲精品国产一区黑色丝袜| 一区二区视频在线| 91激情在线观看| 亚洲一级免费视频| 国产在线88av| av蓝导航精品导航| 99久久夜色精品国产亚洲狼 | 天堂久久午夜av| 精品久久久久久亚洲| 亚洲午夜精品一区二区国产| 久久久久久久片| 26uuu精品一区二区| 日本免费一二三区| 精品裸体舞一区二区三区| 大地资源网3页在线观看| 国产精品久久久久久亚洲影视| 国内精品偷拍| 大西瓜av在线| 国产乱码精品一区二区三区av | 在线观看h网| 成人网在线观看| 99精品视频在线观看播放| 天堂社区在线视频| 国产日韩一级二级三级| 亚洲欧美偷拍一区| 亚洲欧美中文日韩v在线观看| 97蜜桃久久| 精品午夜一区二区三区| 亚洲精品日韩久久| 精品无码人妻少妇久久久久久| 亚洲精品中文字幕在线观看| 国产精品久久久久毛片| 精品国模在线视频| 欧美视频在线视频精品| 一本一本久久a久久精品综合妖精| 日本系列欧美系列| 自拍偷拍你懂的| 欧美日韩一区二区不卡| 伊人免费在线| 国产专区精品视频| 亚洲xxx拳头交| 69久久精品无码一区二区 | 在线精品免费视| 亚洲欧美另类在线观看| 国产精品迅雷| 日本一区二区在线视频观看| 日韩国产欧美在线播放| 男人天堂资源网| 91精品国产综合久久久久久久| 国产成人无吗| 国产精品香蕉视屏| 亚洲综合二区| 欧美激情久久久久久久| 欧美日韩成人综合在线一区二区| 黄网站在线免费看| 国产精品av一区| 国产麻豆综合| 日本高清黄色片| 日韩一卡二卡三卡国产欧美| 啦啦啦中文在线观看日本| 国产在线精品一区二区中文| 久久高清一区| 国产白丝一区二区三区| 日韩亚洲欧美高清| 亚洲精品国产精品国产| 亚洲成人18| 国产福利精品导航| wwwwww国产| 久久亚洲电影天堂| 国产精品主播在线观看| 污污视频网站免费观看| 亚洲欧洲av在线| 色综合免费视频| 国产欧美一区二区三区久久| 国产一区激情| 国产123在线| 日韩三级在线免费观看| 天堂√中文最新版在线| 一本久久a久久精品vr综合| 国产sm精品调教视频网站| 手机av免费观看| 欧美人交a欧美精品| 国产精品羞羞答答在线观看 | 久久亚洲精精品中文字幕早川悠里| 中文字幕人妻互换av久久| 欧美精品18videos性欧| 精品美女久久| 欧美极品jizzhd欧美仙踪林| 欧美又粗又大又爽| 欧美日韩经典丝袜| 亚洲精品影院| 91老师国产黑色丝袜在线| 99久久99久久久精品棕色圆| 国产经典一区二区| 一区免费在线| 男人的午夜天堂|