前言
本文參考Google Java編程規(guī)范和阿里巴巴Java規(guī)范,Android編碼規(guī)范部分參考Android開發(fā)者指南
代碼規(guī)范的好處
- 提高開發(fā)團(tuán)隊(duì)代碼一致性和可讀性,便于其他開發(fā)者理解,溝通和維護(hù)
- 降低怪異bug出現(xiàn)的幾率
- 降低CodeReview時(shí)間成本
- 降低后期維護(hù),升級(jí)成本
- 提高源代碼的可重用性
- 提高開發(fā)效率和開發(fā)質(zhì)量
軟件項(xiàng)目設(shè)計(jì)需求
- 面向接口設(shè)計(jì)
- 面向組件
- 面向服務(wù)設(shè)計(jì)
- 面向修改(易擴(kuò)展,易替換,易刪除,易復(fù)用)
- 面向切面
- 面向動(dòng)態(tài)配置
- 面向事件編程
- 其他設(shè)計(jì)需求,總結(jié)起來都是根據(jù)需求,靈活運(yùn)用設(shè)計(jì)原則和設(shè)計(jì)模式
實(shí)現(xiàn)以上設(shè)計(jì)需求,請(qǐng)參考下文:
一、使用面向?qū)ο笤O(shè)計(jì)原則
二、使用面向?qū)ο笤O(shè)計(jì)模式
一、使用面向?qū)ο笤O(shè)計(jì)原則
二、使用面向?qū)ο笤O(shè)計(jì)模式
參考:
- 面向?qū)ο笤O(shè)計(jì)模式
- 《設(shè)計(jì)模式之禪》
- 《Head First 設(shè)計(jì)模式》
三、Java編碼規(guī)范
3.1 命名規(guī)范
1.【強(qiáng)制】所有編程相關(guān)的命名禁止使用_和$等特殊字符開頭或結(jié)尾。
反例:\_name __name $Object name_ name$ Object$
正例:name
2.【強(qiáng)制】命名應(yīng)該使用英文,禁止中文,禁止拼音+英文,避免拼音。
反例:DaZhePromotion [打折]
getPingfenByName() [評(píng)分]
int 變量 = 3
正例:discount getScoreByName() youku hangzhou alibaba 等通用名稱可視為英文。
3.【強(qiáng)制】類名使用UpperCamelCase風(fēng)格,必須遵從駝峰形式,但以下情形例外:(領(lǐng)域模型的相關(guān)命名)DO / DTO / VO / DAO等。
反例:macroPolo UserDo XMLService TCPUDPDeal TAPromotion
正例:MarcoPolo UserDO XmlService TcpUdpDeal TaPromotion
4.【強(qiáng)制】方法名、參數(shù)名、成員變量、局部變量都統(tǒng)一使用lowerCamelCase風(fēng)格,必須遵從駝峰形式。
反例:LocalValue GetHttpMessage() InputUserId
正例:localValue getHttpMessage() inputUserId
5.【強(qiáng)制】常量命名全部大寫,單詞間用下劃線隔開,力求語(yǔ)義表達(dá)完整清楚,不要嫌名字長(zhǎng)。
反例:public static final int max_stock_count = 10;
正例:public static final int MAX_STOCK_COUNT = 10;
6.【強(qiáng)制】必要的模塊區(qū)分,類型區(qū)分,強(qiáng)制使用前綴prefix和后綴suffix
1、抽象類命名使用 Abstract 或 abs 或 Base開頭;
2、異常類命名使用 Exception 結(jié)尾;
3、測(cè)試類命名以它要測(cè)試的類的名稱開始,以Test結(jié)尾
4、如果使用到了設(shè)計(jì)模式,建議在類名中體現(xiàn)出具體模式
正例:
public class BaseActivity;
public class UserLoginTest;
public class OrderFactory;
public class LoginProxy;
public class ResourceObserver
7.【強(qiáng)制】中括號(hào) [] 是數(shù)組的一部分,必須嚴(yán)格定義
反例:String arry[];
正例:String[] arry;
8.【強(qiáng)制】POJO類中的任何布爾類型的變量,都不要加is,否則部分框架解析會(huì)引起序列化錯(cuò)誤。
反例:boolean isSuccess
它的get方法也是isSuccess(),RPC框架在反向解析的時(shí)候,“以為”對(duì)應(yīng)的
屬性名稱是success,導(dǎo)致屬性獲取不到,進(jìn)而拋出異常
正例:boolean success
9.【強(qiáng)制】包名統(tǒng)一使用小寫,點(diǎn)分隔符之間有且僅有一個(gè)自然語(yǔ)義的英語(yǔ)單詞。包名統(tǒng)一使用單數(shù)形式,但是類名如果有復(fù)數(shù)含義,類名可以使用復(fù)數(shù)形式。
反例:com.alibaba.baselibrary.util
其中baselibrary是由兩個(gè)語(yǔ)義的單詞組合而成。
正例:應(yīng)用工具類包名為com.alibaba.mpp.util
類名為MessageUtils(此規(guī)則參考spring 的框架結(jié)構(gòu))
10.【強(qiáng)制】杜絕完全不規(guī)范的縮寫,避免望文不知義,如果縮寫意義不明確,就寫完整單詞。
11.【強(qiáng)制】接口和實(shí)現(xiàn)類的命名有兩套規(guī)則
1. 對(duì)于Service和DAO類,基于SOA的理念,暴露出來的服務(wù)一定是接口,
內(nèi)部的實(shí)現(xiàn)類用Impl的后綴與接口區(qū)別
正例:CacheServiceImpl實(shí)現(xiàn)CacheService接口
2. 如果是形容能力的接口名稱,取對(duì)應(yīng)的形容詞做接口名(通常是–able的形式)
正例:AbstractTranslator實(shí)現(xiàn) Translatable
12.【推薦】接口類中的方法和屬性不要加任何修飾符號(hào)(public 也不要加),保持代碼的簡(jiǎn)潔性,并加上有效的javadoc注釋。盡量不要在接口里定義變量,如果一定要定義變量,肯定是與接口方法相關(guān),并且是整個(gè)應(yīng)用的基礎(chǔ)常量。
反例:接口方法定義:public abstract void f();
說明:JDK8中接口允許有默認(rèn)實(shí)現(xiàn),那么這個(gè)default方法,是對(duì)所有實(shí)現(xiàn)類都有價(jià)值的默認(rèn)實(shí)現(xiàn)。
正例:接口方法簽名:void f();
接口基礎(chǔ)常量表示:String COMPANY = "alibaba";
13.【強(qiáng)制】枚舉類名建議帶上Enum后綴,枚舉成員名稱需要全大寫,單詞間用下劃線隔開。
正例:枚舉名字:DealStatusEnum;
成員名稱:SUCCESS,UNKOWN_REASON
14.【推薦】特殊業(yè)務(wù)方法命名規(guī)則根據(jù)業(yè)務(wù)定義如下。
Service/DAO層方法命名規(guī)約
1) 獲取單個(gè)對(duì)象的方法用 get 做前綴。
2) 獲取多個(gè)對(duì)象的方法用 list 做前綴。
3) 獲取統(tǒng)計(jì)值的方法用 count 做前綴。
4) 插入的方法用 save(推薦)或insert 做前綴。
5) 刪除的方法用 remove(推薦)或delete 做前綴。
6) 修改的方法用 update 做前綴。
7) 查詢使用 query 做前綴
8) 添加使用 add 做前綴
領(lǐng)域模型命名規(guī)約
1) 數(shù)據(jù)對(duì)象:xxxDO,xxx即為數(shù)據(jù)表名。
2) 數(shù)據(jù)傳輸對(duì)象:xxxDTO,xxx為業(yè)務(wù)領(lǐng)域相關(guān)的名稱。
3) 展示對(duì)象:xxxVO,xxx一般為網(wǎng)頁(yè)名稱。
4) POJO是DO/DTO/BO/VO的統(tǒng)稱,禁止命名成xxxPOJO。
15.【強(qiáng)制】絕對(duì)不允許出現(xiàn)任何魔法值(即未經(jīng)定義的常量)直接出現(xiàn)在代碼中。
反例:
public int getIndexDistance(){
int currentIndex = 10;
return 1 + currentIndex;
}
該例中“1”屬于魔法數(shù)字,根本不知道是什么意思。其他開發(fā)者很難理解程序邏輯。
正例:
public int getIndexDistance(){
int indexOffset = -1;
int currentIndex = 10;
return startIndex + currentIndex;
}
16.【強(qiáng)制】long或者Long初始賦值時(shí),必須使用大寫的L,不能是小寫的l,小寫容易跟數(shù)字1 混淆,造成誤解。
反例:Long a = 2l; 寫的是數(shù)字的21,還是Long型的2?
正例:Long a = 2L;
17.【強(qiáng)制】不要使用一個(gè)常量類維護(hù)所有常量,應(yīng)該按常量功能進(jìn)行歸類,分開維護(hù)。大而全的常量類,不利于理解,也不利于維護(hù)
正例:緩存相關(guān)的常量放在類:CacheConsts下;
系統(tǒng)配置相關(guān)的常量放在類:ConfigConsts下。
18.【推薦】常量的復(fù)用層次有五層:跨應(yīng)用共享常量、應(yīng)用內(nèi)共享常量、子工程內(nèi)共享常量、包內(nèi)共享常量、類內(nèi)共享常量
1) 跨應(yīng)用共享常量:放置在二方庫(kù)中,通常是client.jar中的const目錄下。
2) 應(yīng)用內(nèi)共享常量:放置在一方庫(kù)的modules中的const目錄下。
反例:易懂變量也要統(tǒng)一定義成應(yīng)用內(nèi)共享常量,兩位攻城師在兩個(gè)類中分別定義了表示“是”的變量:
類A中:public static final String YES ="yes";
類B中:public static final String YES = "y";
A.YES.equals(B.YES),預(yù)期是true,但實(shí)際返回為false,導(dǎo)致產(chǎn)生線上問題。
3) 子工程內(nèi)部共享常量:即在當(dāng)前子工程的const目錄下。
4) 包內(nèi)共享常量:即在當(dāng)前包下單獨(dú)的const目錄下。
5) 類內(nèi)共享常量:直接在類內(nèi)部private static final定義。
19.【強(qiáng)制】關(guān)鍵字,大括號(hào),小括號(hào),參數(shù)等語(yǔ)法格式約定
1) 如果是大括號(hào)內(nèi)為空,則簡(jiǎn)潔地寫成{}即可,不需要換行;如果是非空代碼塊則:
2) 左大括號(hào)前不換行。
3) 左大括號(hào)后換行。
4) 右大括號(hào)前換行。
5) 右大括號(hào)后還有else等代碼則不換行;表示終止右大括號(hào)后必須換行。
6) 左括號(hào)和后一個(gè)字符之間不出現(xiàn)空格;同樣,右括號(hào)和前一個(gè)字符 之間也不出現(xiàn)空格
7) if/for/while/switch/do等保留字與左右括號(hào)之間都必須加空格
8) 任何運(yùn)算符左右必須加一個(gè)空格
9) 代碼塊縮進(jìn)4個(gè)空格,如果使用tab縮進(jìn),請(qǐng)?jiān)O(shè)置成1個(gè)tab為4個(gè)空格
正例:
public int quotTest(int a, int b){
if (a == 1) {
b = a + b;
} else{
b = a * 2 + b;
}
return b;
}
20.【推薦】單行字符數(shù)限制不超過120個(gè),超出需要換行,換行時(shí),遵循如下原則:
1) 換行時(shí)相對(duì)上一行縮進(jìn)4個(gè)空格。
2) 運(yùn)算符與下文一起換行。
3) 方法調(diào)用的點(diǎn)符號(hào)與下文一起換行。
4) 在多個(gè)參數(shù)超長(zhǎng),逗號(hào)后進(jìn)行換行。
5) 在括號(hào)前不要換行
21.【強(qiáng)制】方法參數(shù)在定義和傳入時(shí),多個(gè)參數(shù)逗號(hào)后邊必須加空格。
反例:method(a,b,c)
正例:method(a, b, c)
22.【強(qiáng)制】沒有必要增加若干空格來使某一行的字符與上一行的相應(yīng)字符對(duì)齊。
正例:
int a = 3;
long b = 4L;
float c = 5F;
StringBuffer sb = new StringBuffer();
反例:
int a = 3;
long b = 4L;
float c = 5F;
StringBuffer sb = new StringBuffer();
23.【強(qiáng)制】IDE的text file encoding設(shè)置為UTF-8; IDE中文件的換行符使用Unix格式
24.【推薦】方法體內(nèi)的執(zhí)行語(yǔ)句組、變量的定義語(yǔ)句組、不同的業(yè)務(wù)邏輯之間或者不同的語(yǔ)義之間插入一個(gè)空行。相同業(yè)務(wù)邏輯和語(yǔ)義之間不需要插入空行,沒有必要插入多行空格進(jìn)行隔開
25.【強(qiáng)制】OOP原則
1、避免通過一個(gè)類的對(duì)象引用訪問此類的靜態(tài)變量或靜態(tài)方法,無謂增加編譯器解析成本,
直接用類名來訪問即可
2、所有的覆寫方法,必須加@Override注解
反例:getObject()與get0bject()的問題。一個(gè)是字母的O,一個(gè)是數(shù)字的0,加@Override可以
準(zhǔn)確判斷是否覆蓋成功。另外,如果在抽象類中對(duì)方法簽名進(jìn)行修改,其實(shí)現(xiàn)類會(huì)馬上編譯報(bào)錯(cuò)
3、相同參數(shù)類型,相同業(yè)務(wù)含義,才可以使用Java的可變參數(shù),避免使用Object,但除非必要,還是盡量
少用可變參數(shù),不好理解。
4、對(duì)外暴露的接口簽名,原則上不允許修改方法簽名,避免對(duì)接口調(diào)用方產(chǎn)生影響。接口過時(shí)必須加
@Deprecated注解,并清晰地說明采用的新接口或者新服務(wù)是什么
5、不能使用過時(shí)的類或方法
6、Object的equals方法容易拋空指針異常,應(yīng)使用常量或確定有值的對(duì)象來調(diào)用equals。
正例: "test".equals(object);
反例: object.equals("test");
說明:推薦使用java.util.Objects#equals (JDK7引入的工具類)
7、所有的相同類型的包裝類對(duì)象之間值的比較,全部使用equals方法比較。
說明:對(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)行判斷
26.【強(qiáng)制】基本數(shù)據(jù)類型與包裝數(shù)據(jù)類型的使用標(biāo)準(zhǔn)
1) 所有的POJO類屬性必須使用包裝數(shù)據(jù)類型。
2) RPC方法的返回值和參數(shù)必須使用包裝數(shù)據(jù)類型。
3) 所有的局部變量推薦使用基本數(shù)據(jù)類型。
說明:POJO類屬性沒有初值是提醒使用者在需要使用時(shí),必須自己顯式地進(jìn)行賦值,任何NPE問題,
或者入庫(kù)檢查,都由使用者來保證。
正例:數(shù)據(jù)庫(kù)的查詢結(jié)果可能是null,因?yàn)樽詣?dòng)拆箱,用基本數(shù)據(jù)類型接收有NPE風(fēng)險(xiǎn)。
反例:某業(yè)務(wù)的交易報(bào)表上顯示成交總額漲跌情況,即正負(fù)x%,x為基本數(shù)據(jù)類型,調(diào)用的 RPC服務(wù),
調(diào)用不成功時(shí),返回的是默認(rèn)值,頁(yè)面顯示:0%,這是不合理的,應(yīng)該顯示成中劃線-。所以
包裝數(shù)據(jù)類型的null值,能夠表示額外的信息,如:遠(yuǎn)程調(diào)用失敗,異常退出
27.【推薦】定義DO/DTO/VO等POJO類時(shí),不要設(shè)定任何屬性默認(rèn)值。(除非業(yè)務(wù)明確要求需要一個(gè)默認(rèn)值)
反例:某業(yè)務(wù)的DO的gmtCreate默認(rèn)值為newDate();但是這個(gè)屬性在數(shù)據(jù)提取時(shí)并沒有置入具體值,
在更新其它字段時(shí)又附帶更新了此字段,導(dǎo)致創(chuàng)建時(shí)間被修改成當(dāng)前時(shí)間。
28.【推薦】序列化類新增屬性時(shí),請(qǐng)不要修改serialVersionUID字段,避免反序列失?。蝗绻耆患嫒萆?jí),避免反序列化混亂,那么請(qǐng)修改serialVersionUID值。
說明:注意serialVersionUID不一致會(huì)拋出序列化運(yùn)行時(shí)異常
29.【強(qiáng)制】構(gòu)造方法里面禁止加入任何業(yè)務(wù)邏輯,如果有初始化邏輯,請(qǐng)放在init方法中。
30.【強(qiáng)制】POJO類必須寫toString方法。使用工具類source> generate toString時(shí),如果繼承了另一個(gè)POJO類,注意在前面加一下super.toString
說明:在方法執(zhí)行拋出異常時(shí),可以直接調(diào)用POJO的toString()方法打印其屬性值,便于排查問題。
31.【強(qiáng)制】使用索引訪問用String的split方法得到的數(shù)組時(shí),需做最后一個(gè)分隔符后有無內(nèi)容的檢查,否則會(huì)有拋IndexOutOfBoundsException的風(fēng)險(xiǎn)
32.【強(qiáng)制】當(dāng)一個(gè)類有多個(gè)構(gòu)造方法,或者多個(gè)同名方法,這些方法應(yīng)該按順序放置在一起,便于閱讀
33.【推薦】類內(nèi)方法定義順序依次是:公有方法或保護(hù)方法 > 私有方法 > getter/setter方法
34.【推薦】setter方法中,參數(shù)名稱與類成員變量名稱一致,this.成員名=參數(shù)名。在 getter/setter方法中,盡量不要增加業(yè)務(wù)邏輯,增加排查問題難度
【反例】
public IntegergetData(){
if(true) {
return data +100;
} else{
return data- 100;
}
}
35.【強(qiáng)制】循環(huán)體內(nèi),字符串的聯(lián)接方式,使用StringBuilder的append方法進(jìn)行擴(kuò)展
【反例】
String str ="start";
for(int i=0; i<100;i++){
str = str +"hello";
}
36.【強(qiáng)制】慎用Object的clone方法來拷貝對(duì)象。
說明:對(duì)象的clone方法默認(rèn)是淺拷貝,若想實(shí)現(xiàn)深拷貝需要重寫clone方法實(shí)現(xiàn)屬性對(duì)象的拷貝。
37.【推薦】類成員與方法訪問控制從嚴(yán)
1) 如果不允許外部直接通過new來創(chuàng)建對(duì)象,那么構(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) 類成員方法只對(duì)繼承類公開,那么限制為protected。
說明:任何類、方法、參數(shù)、變量,嚴(yán)控訪問范圍。過寬泛的訪問范圍,不利于模塊解耦。思考:如果
是一個(gè)private的方法,想刪除就刪除,可是一個(gè)public的Service方法,或者一個(gè)public的
成員變量,刪除 一下,不得手心冒點(diǎn)汗嗎?變量像自己的小孩,盡量在自己的視線內(nèi),變量作用域
太大,如果無限制的到處跑,那么你會(huì)擔(dān)心的。
38.【強(qiáng)制】集合約定
1、Map/Set的key為自定義對(duì)象時(shí),必須重寫hashCode和equals
正例:
String重寫了hashCode和equals方法,所以我們可以非常愉快地使用String對(duì)象作為key來使用
2、ArrayList的subList結(jié)果不可強(qiáng)轉(zhuǎn)成ArrayList,否則會(huì)拋出ClassCastException 異常
java.util.RandomAccessSubList cannot be cast to java.util.ArrayList;
說明:subList 返回的是 ArrayList 的內(nèi)部類 SubList,并不是 ArrayList ,而是
ArrayList 的一個(gè)視圖,對(duì)于SubList子列表的所有操作最終會(huì)反映到原列表上。
3、在subList場(chǎng)景中,高度注意對(duì)原集合元素個(gè)數(shù)的修改,會(huì)導(dǎo)致子列表的遍歷、增加、刪除均
產(chǎn)生ConcurrentModificationException 異常
4、使用集合轉(zhuǎn)數(shù)組的方法,必須使用集合的toArray(T[] array),傳入的是類型完全一樣的數(shù)組,
大小就是list.size()
反例:
直接使用toArray無參方法存在問題,此方法返回值只能是Object[]類,若強(qiáng)轉(zhuǎn)其它類型數(shù)組將
出現(xiàn)ClassCastException錯(cuò)誤。
正例:
List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array =list.toArray(array);
說明:使用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ù)一致
39.【強(qiáng)制】在JDK7版本以上,Comparator要滿足自反性,傳遞性,對(duì)稱性,不然Arrays.sort,Collections.sort會(huì)報(bào)IllegalArgumentException異常
說明:
1) 自反性:x,y的比較結(jié)果和y,x的比較結(jié)果相反。
2) 傳遞性:x>y,y>z,則x>z。
3) 對(duì)稱性:x=y,則x,z比較結(jié)果和y,z比較結(jié)果相同。反例:下例中沒有處理相等的情況,
實(shí)際使用中可能會(huì)出現(xiàn)異常:
new Comparator<Student>(){
@Override
public int compare(Student o1, Student o2){
return o1.getId() > o2.getId() ? 1 :-1;
}
}
40.【推薦】集合初始化時(shí),盡量指定集合初始值大小。說明:ArrayList盡量使用ArrayList(int initialCapacity) 初始化。
類似注意HashMap的擴(kuò)容死鏈,導(dǎo)致CPU飆升的問題。
41.【推薦】使用entrySet遍歷Map類集合KV,而不是keySet方式進(jìn)行遍歷,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值組合集合
42.【強(qiáng)制】高度注意Map類集合K/V能不能存儲(chǔ)null值的情況,并且作為KEY的對(duì)象必須同時(shí)重寫hashCode和equals方法
43.【推薦】合理利用好集合的有序性(sort)和穩(wěn)定性(order),避免集合的無序性(unsort)和不穩(wěn)定性(unorder)帶來的負(fù)面影響。
說明:穩(wěn)定性指集合每次遍歷的元素次序是一定的。有序性是指遍歷的結(jié)果是按某種比較規(guī)則依次排列的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是 order/sort
44.【推薦】利用Set元素唯一的特性,可以快速對(duì)另一個(gè)集合進(jìn)行去重操作,避免使用List的 contains方法進(jìn)行遍歷去重操作
45.【推薦】SimpleDateFormat 是線程不安全的類,一般不要定義為static變量,如果定義為 static,必須加鎖,或者使用DateUtils工具類。
正例:注意線程安全,使用DateUtils。亦推薦如下處理:
private static final ThreadLocal<DateFormat> df =
new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue(){
return newSimpleDateFormat("yyyy-MM-dd");
}
};
說明:如果是JDK8的應(yīng)用,可以使用instant代替Date,Localdatetime代替Calendar,
Datetimeformatter代替Simpledateformatter,官方給出的解釋:simple beautiful
strong immutable thread-safe
46.【推薦】創(chuàng)建線程或線程池時(shí)請(qǐng)指定有意義的線程名稱,方便出錯(cuò)時(shí)回溯
正例:
public class TimerTaskThread extends Thread{
publicTimerTaskThread(){
super.setName("TimerTaskThread");
}
}
47.【推薦】線程池不允許使用Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。
說明:Executors各個(gè)方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要問題是堆積的請(qǐng)求處理隊(duì)列可能會(huì)耗費(fèi)非常大的內(nèi)存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要問題是線程數(shù)最大數(shù)是Integer.MAX_VALUE,可能會(huì)創(chuàng)建數(shù)量非常多的線程,甚至OOM。
48.【強(qiáng)制】使用CountDownLatch進(jìn)行異步轉(zhuǎn)同步操作,每個(gè)線程退出前必須調(diào)用countDown方法,線程執(zhí)行代碼注意catch異常,確保countDown方法可以執(zhí)行,避免主線程無法執(zhí)行至 countDown方法,直到超時(shí)才返回結(jié)果。
說明:注意,子線程拋出異常堆棧,不能在主線程try-catch到。
49.【強(qiáng)制】避免Random實(shí)例被多線程使用,雖然共享該實(shí)例是線程安全的,但會(huì)因競(jìng)爭(zhēng)同一seed 導(dǎo)致的性能下降。
說明:Random實(shí)例包括java.util.Random 的實(shí)例或者 Math.random()實(shí)例。
正例:
在JDK7之后,可以直接使用API ThreadLocalRandom,在 JDK7之前,可以做到每個(gè)線程一個(gè)實(shí)例
50.【強(qiáng)制】通過雙重檢查鎖(double-checked locking)(在并發(fā)場(chǎng)景)實(shí)現(xiàn)延遲初始化的優(yōu)化問題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問題解決方案中較為簡(jiǎn)單一種(適用于jdk5及以上版本),將目標(biāo)屬性聲明為 volatile型。
反例:
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper ==null)
synchronized(this) {
if (helper== null)
helper = newHelper();
}
return helper;
}
// other functions and members...
}
說明:將反例中helper的屬性聲明為private volatile Helper helper= null,即可解決該問題。
51.【強(qiáng)制】volatile解決多線程內(nèi)存不可見問題。對(duì)于一寫多讀,是可以解決變量同步問題,但是如果多寫,同樣無法解決線程安全問題。
解決方案:
如果想取回count++數(shù)據(jù),使用如下類實(shí)現(xiàn)
AtomicInteger count = new AtomicInteger();
count.addAndGet(1);
另外count++操作如果是JDK8,推薦使用LongAdder對(duì)象,
比AtomicLong性能更好(減少樂觀鎖的重試次數(shù))。
52.【推薦】ThreadLocal無法解決共享對(duì)象的更新問題,ThreadLocal對(duì)象建議使用static修飾。這個(gè)變量是針對(duì)一個(gè)線程內(nèi)所有操作共有的,所以設(shè)置為靜態(tài)變量,所有此類實(shí)例共享此靜態(tài)變量 ,也就是說在類第一次被使用時(shí)裝載,只分配一塊存儲(chǔ)空間,所有此類的對(duì)象(只要是這個(gè)線程內(nèi)定義的)都可以操控這個(gè)變量
53.【推薦】在一個(gè)switch塊內(nèi),每個(gè)case要么通過break/return來終止,要么注釋說明程序?qū)⒗^續(xù)執(zhí)行到哪一個(gè)case為止;在一個(gè)switch塊內(nèi),都必須包含一個(gè)default語(yǔ)句并且放在最后,即使它什么代碼也沒有
54.【強(qiáng)制】在if/else/for/while/do語(yǔ)句中必須使用大括號(hào),即使只有一行代碼,避免使用下面的形式:if (condition) statements;
55.【推薦】推薦盡量少用else, if-else的方式可以改寫成:
if(condition){
…
return obj;
}
// 接著寫else的業(yè)務(wù)邏輯代碼;
說明:如果使用要if-else if-else方式表達(dá)邏輯,【強(qiáng)制】請(qǐng)勿超過3層,超過請(qǐng)使用: 狀態(tài)設(shè)計(jì)模式
56.【強(qiáng)制】除常用方法(如getXxx/isXxx)等外,不要在條件判斷中執(zhí)行復(fù)雜的語(yǔ)句,以提高可讀性。
57.【推薦】try-catch移至循環(huán)體外
58.【推薦】方法中需要進(jìn)行參數(shù)校驗(yàn)的場(chǎng)景:
1) 調(diào)用頻次低的方法。
2) 執(zhí)行時(shí)間開銷很大的方法,參數(shù)校驗(yàn)時(shí)間幾乎可以忽略不計(jì),但如果因?yàn)閰?shù)錯(cuò)誤導(dǎo)致中間執(zhí)行回退,
或者錯(cuò)誤,那得不償失。
3) 需要極高穩(wěn)定性和可用性的方法。
4) 對(duì)外提供的開放接口,不管是RPC/API/HTTP接口
59.【推薦】方法中不需要參數(shù)校驗(yàn)的場(chǎng)景:
1) 極有可能被循環(huán)調(diào)用的方法,不建議對(duì)參數(shù)進(jìn)行校驗(yàn)。但在方法說明里必須注明外部參數(shù)檢查。
2) 底層的方法調(diào)用頻度都比較高,一般不校驗(yàn)。畢竟是像純凈水過濾的最后一道,參數(shù)錯(cuò)誤不太可能
到底層才會(huì)暴露問題。一般DAO層與Service層都在同一個(gè)應(yīng)用中,部署在同一臺(tái)服務(wù)器中,所以DAO的
參數(shù)校驗(yàn),可以省略。
3) 被聲明成private只會(huì)被自己代碼所調(diào)用的方法,如果能夠確定調(diào)用方法的代碼傳入?yún)?shù)已經(jīng)做過檢查
或者肯定不會(huì)有問題,此時(shí)可以不校驗(yàn)參數(shù)
60.【強(qiáng)制】類、類屬性、類方法的注釋必須使用javadoc規(guī)范,使用/*內(nèi)容/格式,不得使用//xxx方式。
說明:在IDE編輯窗口中,javadoc方式會(huì)提示相關(guān)注釋,生成javadoc可以正確輸出相應(yīng)注釋;
在IDE中,工程調(diào)用方法時(shí),不進(jìn)入方法即可懸浮提示方法、參數(shù)、返回值的意義,提高閱讀效率
61.【推薦】所有的抽象方法(包括接口中的方法)必須要用javadoc注釋、除了返回值、參數(shù)、異常說明外,還必須指出該方法做什么事情,實(shí)現(xiàn)什么功能。
62.【強(qiáng)制】所有的類都必須添加創(chuàng)建者信息
63.【強(qiáng)制】方法內(nèi)部單行注釋,在被注釋語(yǔ)句上方另起一行,使用//注釋。方法內(nèi)部多行注釋使用/* */注釋,注意與代碼對(duì)齊
64.【強(qiáng)制】所有的枚舉類型字段必須要有注釋,說明每個(gè)數(shù)據(jù)項(xiàng)的用途
65.【推薦】與其“半吊子”英文來注釋,不如用中文注釋把問題說清楚。專有名詞、關(guān)鍵字,保持英文原文即可
反例:“TCP連接超時(shí)”解釋成“傳輸控制協(xié)議連接超時(shí)”,理解反而費(fèi)腦筋
66.【推薦】代碼修改的同時(shí),注釋也要進(jìn)行相應(yīng)的修改,尤其是參數(shù)、返回值、異常、核心邏輯等的修改
67.【推薦】注釋掉的代碼盡量要配合說明,而不是簡(jiǎn)單的注釋掉。
說明:代碼被注釋掉有兩種可能性:1)后續(xù)會(huì)恢復(fù)此段代碼邏輯。2)永久不用。前者如果沒有備注信息,
難以知曉注釋動(dòng)機(jī)。后者建議直接刪掉(代碼倉(cāng)庫(kù)保存了歷史代碼)
68.【推薦】好的命名、代碼結(jié)構(gòu)是自解釋的,注釋力求精簡(jiǎn)準(zhǔn)確、表達(dá)到位。避免出現(xiàn)注釋的一個(gè)極端:過多過濫的注釋,代碼的邏輯一旦修改,修改注釋是相當(dāng)大的負(fù)擔(dān)
反例:
// put elephant into fridge
put(elephant,fridge);
方法名put,加上兩個(gè)有意義的變量名elephant和fridge,已經(jīng)說明了這是在干什么,語(yǔ)義清晰的
代碼不需要額外的注釋
69.【推薦】待辦事宜(TODO):( 標(biāo)記人,標(biāo)記時(shí)間,[預(yù)計(jì)處理時(shí)間])
70.【推薦】錯(cuò)誤,不能工作(FIXME):(標(biāo)記人,標(biāo)記時(shí)間,[預(yù)計(jì)處理時(shí)間])
71.【強(qiáng)制】在使用正則表達(dá)式時(shí),利用好其預(yù)編譯功能,可以有效加快正則匹配速度。
說明:不要在方法體內(nèi)定義:Pattern pattern = Pattern.compile(規(guī)則);
72.【強(qiáng)制】避免用Apache Beanutils進(jìn)行屬性的copy。
說明:Apache BeanUtils性能較差,可以使用其他方案
比如SpringBeanUtils, CglibBeanCopier。
73.【強(qiáng)制】注意 Math.random() 這個(gè)方法返回是double類型,注意取值范圍 0≤x<1(能夠取到零值,注意除零異常),如果想獲取整數(shù)類型的隨機(jī)數(shù),不要將x放大10的若干倍然后取整,直接使用Random對(duì)象的nextInt或者nextLong方法
74.【強(qiáng)制】獲取當(dāng)前毫秒數(shù):System.currentTimeMillis(); 而不是new Date().getTime(); 說明:如果想獲取更加精確的納秒級(jí)時(shí)間值,用System.nanoTime。在JDK8中,針對(duì)統(tǒng)計(jì)時(shí)間等場(chǎng)景,推薦使用Instant類
75.【推薦】任何數(shù)據(jù)結(jié)構(gòu)的使用都應(yīng)限制大小。
說明:這點(diǎn)很難完全做到,但很多次的故障都是因?yàn)閿?shù)據(jù)結(jié)構(gòu)自增長(zhǎng),結(jié)果造成內(nèi)存被吃光
76.【推薦】對(duì)于“明確停止使用的代碼和配置”,如方法、變量、類、配置文件、動(dòng)態(tài)配置屬性等要堅(jiān)決從程序中清理出去,避免造成過多垃圾。清理這類垃圾代碼是技術(shù)氣場(chǎng),不要有這樣的觀念:“不做不錯(cuò),多做多錯(cuò)
77.【強(qiáng)制】finally塊必須對(duì)資源對(duì)象、流對(duì)象進(jìn)行關(guān)閉,有異常也要做try-catch。
說明:如果JDK7,可以使用try-with-resources方法
78.【強(qiáng)制】不能在finally塊中使用return,finally塊中的return返回后方法結(jié)束執(zhí)行,不會(huì)再執(zhí)行try塊中的return語(yǔ)句。
79.【強(qiáng)制】方法的返回值可以為null,不強(qiáng)制返回空集合,或者空對(duì)象等,必須添加注釋充分說明什么情況下會(huì)返回null值。調(diào)用方需要進(jìn)行null判斷防止NPE問題。
說明:本規(guī)約明確防止NPE是調(diào)用者的責(zé)任。即使被調(diào)用方法返回空集合或者空對(duì)象,對(duì)調(diào)用者來說,
也并非高枕無憂,必須考慮到遠(yuǎn)程調(diào)用失敗,運(yùn)行時(shí)異常等場(chǎng)景返回null的情況
80.【強(qiáng)制】防止NPE,是程序員的基本修養(yǎng),注意NPE產(chǎn)生的場(chǎng)景
1) 返回類型為包裝數(shù)據(jù)類型,有可能是null,返回int值時(shí)注意判空。
反例:public int f(){return Integer對(duì)象},如果為null,自動(dòng)解箱拋NPE。
2) 數(shù)據(jù)庫(kù)的查詢結(jié)果可能為null。
3) 集合里的元素即使isNotEmpty,取出的數(shù)據(jù)元素也可能為null。
4) 遠(yuǎn)程調(diào)用返回對(duì)象,一律要求進(jìn)行NPE判斷。
5) 對(duì)于Session中獲取的數(shù)據(jù),建議NPE檢查,避免空指針。
6) 避免級(jí)聯(lián)調(diào)用obj.getA().getB().getC();一連串調(diào)用,易產(chǎn)生NPE。
81.【強(qiáng)制】避免出現(xiàn)重復(fù)的代碼(Don’tRepeat Yourself),即DRY原則。
正例:
一個(gè)類中有多個(gè)public方法,都需要進(jìn)行數(shù)行相同的參數(shù)校驗(yàn)操作,這個(gè)時(shí)候請(qǐng)抽?。? private boolean checkParam(DTO dto){...}
說明:隨意復(fù)制和粘貼代碼,必然會(huì)導(dǎo)致代碼的重復(fù),在以后需要修改時(shí),需要修改所有的副本,
容易遺漏。必要時(shí)抽取共性方法,或者抽象公共類,甚至是共用模塊。
82.【強(qiáng)制】應(yīng)用中不可直接使用日志系統(tǒng)(Log4j、Logback)中的API,而應(yīng)依賴使用日志框架SLF4J中的API,使用門面模式的日志框架,有利于維護(hù)和各個(gè)類的日志處理方式統(tǒng)一。
83.【推薦】日志文件推薦至少保存15天,因?yàn)橛行┊惓>邆湟浴爸堋睘轭l次發(fā)生的特點(diǎn)
84.【推薦】謹(jǐn)慎地記錄日志。生產(chǎn)環(huán)境禁止輸出debug日志;有選擇地輸出info日志;如果使用warn來記錄剛上線時(shí)的業(yè)務(wù)行為信息,一定要注意日志輸出量的問題,避免把服務(wù)器磁盤撐爆,并記得及時(shí)刪除這些觀察日志
說明:大量地輸出無效日志,不利于系統(tǒng)性能提升,也不利于快速定位錯(cuò)誤點(diǎn)。紀(jì)錄日志時(shí)請(qǐng)思考:
這些日志真的有人看嗎?看到這條日志你能做什么?能不能給問題排查帶來好處?
85.【強(qiáng)制】工具類二方庫(kù)已經(jīng)提供的,盡量不要在本應(yīng)用中編程實(shí)現(xiàn)。
1)json操作:fastjson
2)md5操作:commons-codec
3)工具集合:Guava包
4)數(shù)組操作:ArrayUtils(org.apache.commons.lang3.ArrayUtils)
5)集合操作:CollectionUtils(org.apache.commons.collections4.CollectionUtils)
6)除上面以外還有NumberUtils、DateFormatUtils、DateUtils
等優(yōu)先使用 org.apache.commons.lang3這個(gè)包下的,
不要使用org.apache.commons.lang包下面的。原因是commons.lang這個(gè)包是從JDK1.2開始
支持的所以很多1.5/1.6的特性是不支持的,例如:泛型
86.【強(qiáng)制】穩(wěn)定可追溯原則。每個(gè)版本的變化應(yīng)該被記錄,二方庫(kù)由誰(shuí)維護(hù),源碼在哪里,都需要能方便查到。除非用戶主動(dòng)升級(jí)版本,否則公共二方庫(kù)的行為不應(yīng)該發(fā)生變化.
87.【強(qiáng)制】可被用戶直接訪問的功能必須進(jìn)行權(quán)限控制校驗(yàn)。說明:防止沒有做權(quán)限控制就可隨意訪問、操作別人的數(shù)據(jù),比如查看、修改別人的訂單
88.【強(qiáng)制】用戶敏感數(shù)據(jù)禁止直接展示,必須對(duì)展示數(shù)據(jù)脫敏。說明:支付寶中查看個(gè)人手機(jī)號(hào)碼會(huì)顯示成:158****9119,隱藏中間4位,防止隱私泄露
89.【強(qiáng)制】用戶輸入的SQL參數(shù)嚴(yán)格使用參數(shù)綁定或者M(jìn)ETADATA字段值限定,防止SQL注入,禁止字符串拼接SQL訪問數(shù)據(jù)庫(kù)
90.【強(qiáng)制】用戶請(qǐng)求傳入的任何參數(shù)必須做有效性驗(yàn)證。
說明:忽略參數(shù)校驗(yàn)可能導(dǎo)致:
1) page size過大導(dǎo)致內(nèi)存溢出
2) 惡意order by導(dǎo)致數(shù)據(jù)庫(kù)慢查詢
3) 正則輸入源串拒絕服務(wù)ReDOS
4) 任意重定向
5) SQL注入
6) Shell注入
7) 反序列化注入
91.【強(qiáng)制】表單、AJAX提交必須執(zhí)行CSRF安全過濾。
說明:CSRF(Cross-siterequest forgery)跨站請(qǐng)求偽造是一類常見編程漏洞。對(duì)于存在CSRF漏洞
的應(yīng)用/網(wǎng)站,攻擊者可以事先構(gòu)造好URL,只要受害者用戶一訪問,后臺(tái)便在用戶不知情情況下對(duì)數(shù)據(jù)庫(kù)
中用戶參數(shù)進(jìn)行相應(yīng)修改
92.【強(qiáng)制】URL外部重定向傳入的目標(biāo)地址必須執(zhí)行白名單過濾
93.【強(qiáng)制】在使用平臺(tái)資源,譬如短信、郵件、電話、下單、支付,必須實(shí)現(xiàn)正確的防重放限制,如數(shù)量限制、疲勞度控制、驗(yàn)證碼校驗(yàn),避免被濫刷、資損。
說明:如注冊(cè)時(shí)發(fā)送驗(yàn)證碼到手機(jī),如果沒有限制次數(shù)和頻率,那么可以利用此功能騷擾到其它用戶,
并造成短信平臺(tái)資源浪費(fèi)
94.【強(qiáng)制】發(fā)貼、評(píng)論、發(fā)送即時(shí)消息等用戶生成內(nèi)容的場(chǎng)景必須實(shí)現(xiàn)防刷、文本內(nèi)容違禁詞過濾等風(fēng)控策略
四、Android編碼規(guī)范
<b>名詞解釋:
1)resPrefix :資源的前綴,用于區(qū)分不同module中的資源,每個(gè)module都需要有一個(gè)區(qū)別于其他module的資源前綴,避免資源沖突。</b>
1、【推薦】常用縮寫
| 源詞 | 縮寫 | 代碼示例 | XML示例(如果不作文件名,只是id,有去掉縮寫部分) |
|---|---|---|---|
| LinearLayout | ll | llUserTitle | resPrefix_ll_user_title.xml |
| RelativeLayout | rl | rlUserTitle | resPrefix_rl_user_title.xml |
| FrameLayout | fl | flUserTitle | resPrefix_fl_user_title.xml |
| TableLayout | tl | tlUserTitle | resPrefix_tl_user_title.xml |
| ConstraintLayout | cl | clUserTitle | resPrefix_cl_user_title.xml |
| Button | btn | btnUserLogin | resPrefix_btn_user_login |
| ImageButton | imb | imbUserLogin | resPrefix_imb_user_login |
| TextView | tv | tvUserName | resPrefix_tv_user_name |
| EditText | edit | editUserName | resPrefix_edit_user_name |
| ListView | list | lvCity | resPrefix_list_city |
| ImageView | iv | ivValidateCode | resPrefix_iv_validate_code |
| GridView | grid | gridUsers | resPrefix_grid_users |
1、【推薦】資源縮寫匯總
參考:Android 資源
有的縮寫,使用時(shí)當(dāng)做前綴prefix; 有的縮寫則需要當(dāng)做后綴sufix來使用
| 資源類型 | 縮寫 | 前綴后綴 | xml示例(R.XXX類型保持不重復(fù)) |
|---|---|---|---|
| activity | act | 前綴 | resPrefix_act_splash |
| service | service | 后綴 | |
| ContentProvicer | provider | 后綴 | |
| BroadCastRecever | recever | 后綴 | |
| Fragment | frag | 前綴 | resPrefix_frag_splash_title |
| Utils | util | 后綴 | xxxUtil |
| Manager | manager | 后綴 | xxxManager |
| helper | helper | 后綴 | xxxHelper |
| Dialog | dialog | 前綴 | resPrefix_dialog_update |
| window | window | 前綴 | resPrefix_window_xxxxx |
| Popupwindow | popup | 前綴 | resPrefix_popup_xxxxx |
| adapter | item | 前綴 | resPrefix_item_xxxx |
| include | include | 后綴 | resPrefix_splash_adv_include |
| merge | merge | 后綴 | resPrefix_splash_title_merge |
| 通用資源 | base | 前綴 | 例如字符串資源 resPrefix_base_confirm = “確定” |
| 背景圖片 | bg | 前綴 | resPrefix_bg_splash_title |
| 背景顏色 | bg | 前綴 | resPrefix_bg_splash |
| style | style | 前綴 | resPrefix_style_app |
| strings | 不添加縮寫 | resPrefix_user_name | |
| animator | 不添加縮寫 | resPrefix_push_out_bottom.xml | |
| layout | 不添加縮寫 | resPrefix_user_profile | |
| menu | menu | 前綴 | resPrefix_menu_user_cities |
| arrys | arrys | 前綴 | resPrefix_arrys_user_name |
| raw | raw | 前綴 | resPrefix_raw_user_logo |
| icon | ic | 前綴 | resPrefix_ic_app |
| color | color | resPrefix_sub_title | |
| ColorStateList | selector | 后綴 | resPrefix_user_name_selector |
| divider | divider | 后綴 | resPrefix_user_name_divider |
| BitmapDrawable | 不添加縮寫 | resPrefix_top_adv | |
| NinePatchDrawable | 自帶縮寫 | resPrefix_bg_top.9.png | |
| layerDrawable | layer 或 layer_list | 后綴 | resPrefix_wifi_img_layer |
| StateListDrawable | selector | 后綴 | resPrefix_user_name_selector |
| LevelListDrawable | level 或 level_list | 后綴 | resPrefix_company_logo_level |
| transitionDrawable | transition | 后綴 | resPrefix_company_logo_transition |
| insetDrawable | inset | 后綴 | resPrefix_company_logo_inset |
| clipDrawable | clip | 后綴 | resPrefix_company_logo_clip |
| scaleDrawable | scale | 后綴 | resPrefix_company_logo_scale |
| shapeDrawable | shape | 后綴 | resPrefix_company_logo_shape |
| scaleDrawable | scale | 后綴 | resPrefix_company_logo_scale |
| error | error | 前綴 | resPrefix_error_xxxxx |
| message | msg | 前綴 | resPrefix_msg_xxxxx |
| sub | sub | 前綴 | resPrefix_sub_xxxxx |
| first | first | 后綴 | resPrefix_xxxxx_first |
| last | last | 后綴 | resPrefix_xxxxx_last |
| previous | prev | 后綴 | resPrefix_xxxxx_prev |
| next | next | 后綴 | resPrefix_xxxxx_next |
| current | curr | 后綴 | resPrefix_xxxxx_curr |
1、【強(qiáng)制】多狀態(tài)資源,添加后綴來標(biāo)識(shí)狀態(tài)
| 狀態(tài) | 后綴 |
|---|---|
| 正常狀態(tài) | _normal |
| 按壓狀態(tài) | _pressed |
| 焦點(diǎn)狀態(tài) | _focused |
| 選中狀態(tài) | _checked 或 _selected |
| 激活狀態(tài) | _activited |
| 可選狀態(tài) | _checkable 或 _selectable |
| 懸停狀態(tài) | _hovered |
| 禁用狀態(tài) | _disabled |