
下載.jpeg
命名風(fēng)格
-
類名使用UpperCamelCase風(fēng)格,但下列情形除外:
- DO: Data Object. 與數(shù)據(jù)庫表結(jié)構(gòu)一一對(duì)應(yīng),通過DAO層向上傳輸數(shù)據(jù)源對(duì)象
- BO: Business Object,業(yè)務(wù)對(duì)象. 由Service層輸出的封裝業(yè)務(wù)邏輯的對(duì)象
- DTO: Data Transfer Object,數(shù)據(jù)傳輸對(duì)象. Service或Manager向外傳輸?shù)膶?duì)象
- VO: View Object,顯示對(duì)象. 通常是Web向模板渲染引擎層傳輸?shù)膶?duì)象
- AO: Application Object,應(yīng)用對(duì)象. 在Web層與Service層之間抽象復(fù)用的對(duì)象模型
- PO: POJO縮寫,Plain Ordinary Java Object. 專指只有setter/getter/toString的簡單類,包括DO,DTO,BO,VO等, 禁止使用xxxPOJO來命名
- UID
- 方法名,參數(shù)名,成員變量,局部變量都統(tǒng)一使用lowerCamelcase風(fēng)格
- 常量命名全部大寫,單詞間用下劃線隔開, 力求語義表達(dá)完整清楚,不要嫌名字長
- 抽象類命名使用Abstract或者Base開頭
- 異常類命名使用Exception結(jié)尾
- 測(cè)試類命名要以要測(cè)試的類的名稱命名,以Test結(jié)尾
- 類型與中括號(hào)緊挨來表示數(shù)組
- POJO類中布爾類型的變量都不要加is前綴,在部分框架中會(huì)引起序列化錯(cuò)誤
- 包名統(tǒng)一使用小寫,點(diǎn)分隔符之間有且僅有一個(gè)自然語義的英語單詞.包名統(tǒng)一使用單數(shù)形式.但是類名如果有復(fù)數(shù)含義,可以使用復(fù)數(shù)形式
- 杜絕不規(guī)范的縮寫,避免望文不知義
- 為了達(dá)到代碼自解釋的目標(biāo),任何自定義的編程元素在命名時(shí),使用盡量完整的單詞組合來表達(dá)含義
- 在常量與變量命名的同時(shí),表示類型的名詞放在詞尾,以提升辨識(shí)度
- 如果模塊,接口,類,方法使用了設(shè)計(jì)模式,在命名時(shí)需要體現(xiàn)出設(shè)計(jì)模式
- 接口類中的方法和屬性不要加任何修飾符號(hào)(不要加public), 保持代碼的簡潔
- 盡量不要在接口中定義變量,如果一定要定義變量,一定是與接口方法有關(guān)的,并且是整個(gè)應(yīng)用的基礎(chǔ)變量
- 接口方法簽名: void commit()
- 接口基礎(chǔ)常量: String COMPANY="Oxford"
-
接口和實(shí)現(xiàn)類:
- 對(duì)于Service和DAO類,基于SOA的理念,暴露出來的服務(wù)一定是接口,內(nèi)部的實(shí)現(xiàn)類用Impl的后綴與接口的區(qū)別
- 如果是形容能力的接口名稱,去對(duì)應(yīng)的形容詞為接口(-able的形式)
- 枚舉類帶上Enum后綴,枚舉成員名稱需全部大寫
- 枚舉類是特殊的類,域成員均為常量,且構(gòu)造方法被默認(rèn)強(qiáng)制是私有的
-
各層命名規(guī)范:
-
Service或者DAO層方法命名規(guī)范:
- 獲取單個(gè)對(duì)象的方法用get做前綴
- 獲取多個(gè)對(duì)象的方法用list做前綴 ,復(fù)數(shù)形式結(jié)尾
- 獲取統(tǒng)計(jì)值的方法用count做前綴
- 插入方法使用save或者insert做前綴
- 刪除的方法使用remove或者delete做前綴
- 修改的方法使用update做前綴
-
領(lǐng)域模型命名規(guī)范:
- 數(shù)據(jù)對(duì)象: XxxDO,Xxx為數(shù)據(jù)表名
- 數(shù)據(jù)傳輸對(duì)象: XxxDTO,Xxx為業(yè)務(wù)領(lǐng)域相關(guān)的名稱
- 展示對(duì)象: XxxVO,xxx一般為網(wǎng)頁名稱
- POJO為DO,DTO,BO,VO的統(tǒng)稱,禁止命名成XxxPOJO
-
Service或者DAO層方法命名規(guī)范:
常量定義
- 不允許任何未經(jīng)預(yù)先定義的常量出現(xiàn)在代碼中
- 在long或者Long賦值時(shí),數(shù)值后使用大寫的L, 不能是小寫的l. 因?yàn)樾懭菀缀蛿?shù)字1混淆,造成誤解
- 不要使用一個(gè)常量類維護(hù)所有常量,要按常量的功能進(jìn)行歸類,分開維護(hù)
- 大而全的常量類雜亂無章,使用查找功能才能定位到修改的常量,不利于理解和維護(hù)
-
常量的復(fù)用層次有五層:
- 跨應(yīng)用共享常量: 放置在二方庫中,通常是client.jar中的constant目錄下
- 應(yīng)用類共享常量 放置在一方庫中,通常是子模塊中的constant目錄下
- 子工程內(nèi)共享常量 在當(dāng)前子工程的constant目錄下
- 包內(nèi)共享常量 在當(dāng)前包的constant目錄下
- 類內(nèi)共享常量 直接在類內(nèi)部private static final定義
- 如果變量值僅在一個(gè)固定范圍內(nèi)變化,使用enum類型定義?
- 如果存在名稱之外的延伸屬性應(yīng)使用enum類型,比如季節(jié),表示一年中第幾個(gè)季節(jié):
public enum SeasonEnum {
SPRING(1),SUMMER(2),AUTUMN(3),WINTER(4);
private int seq;
SeasonEnum(int seq) {
this.seq=seq;
}
}
代碼格式
-
大括號(hào)的使用約定:
- 如果大括號(hào)內(nèi)為空,則簡潔地寫成 { } 即可,不需要換行
-
如果是非空代碼塊:
- 左大括號(hào)前不換行
- 左大括號(hào)后換行
- 右大括號(hào)前換行
- 右大括號(hào)后如果還有else則不換行
- 表示終止的右大括號(hào)后必須換行
-
小括號(hào)的使用約定:
- 左小括號(hào)和字符之間不要出現(xiàn)空格
- 右小括號(hào)和字符之間也不要出現(xiàn)空格
- 左大括號(hào)之前需要空格
- if,for,while,switch,do等保留字與括號(hào)之間都必須加空格
-
任何二目,三目運(yùn)算符左右兩邊都需要加一個(gè)空格
- 運(yùn)算符包括:
- 賦值運(yùn)算符 :=
- 邏輯運(yùn)算符 :&&
- 加減乘除符號(hào)
- 運(yùn)算符包括:
- 采用4個(gè)空格進(jìn)行縮進(jìn)
- 注釋的雙斜線與注釋內(nèi)容之間有且僅有一個(gè)空格
- 方法參數(shù)在定義和傳入時(shí),多個(gè)參數(shù)逗號(hào)后面必須加空格
-
單個(gè)方法的總行數(shù)不要超過80行:
- 除注釋之外的方法簽名,左右大括號(hào),方法內(nèi)代碼,空行,回車及任何不可見字符的總行數(shù)不超過80行
- 代碼邏輯分清紅花和綠葉,個(gè)性和共性:
- 綠葉邏輯單獨(dú)出來成為額外的方法,使主干代碼更加清晰
- 共性邏輯抽取成共性方法,便于復(fù)用和維護(hù)
- 不需要增加若干空格來使某一行的字符與上一行對(duì)應(yīng)位置的字符對(duì)齊
- 不同邏輯,不同語義,不同業(yè)務(wù)代碼之間只需要插入一個(gè)空行分割來提升可讀性即可
OPP規(guī)約
- 避免通過一個(gè)類的對(duì)象引用訪問類的靜態(tài)變量和靜態(tài)方法,這會(huì)增加編譯器的解析成本,直接使用類名訪問即可
- 所有的覆寫方法,必須加 @Override
-
相同參數(shù)類型,相同業(yè)務(wù)含義,才可以使用Java的可變參數(shù),避免可變參數(shù)使用Object類型
- 可變參數(shù)必須放置在參數(shù)列表的最后, 建議盡量不要用可變參數(shù)編程
- 外部正在調(diào)用的或者二方庫依賴的接口,不允許修改方法簽名(方法名和參數(shù)列表),避免對(duì)接口的調(diào)用方產(chǎn)生影響 .接口過時(shí)必須加上 ==@Deprecated== 注解,并清晰地說明采用的新接口和新服務(wù)是什么
-
不能使用過時(shí)的類或方法:
- 接口的提供方既然明確是過時(shí)接口,那么有義務(wù)提供新接口
- 作為調(diào)用方,有義務(wù)考證過時(shí)方法的新實(shí)現(xiàn)是什么
-
Object的equals方法容易拋出空指針異常,應(yīng)使用常量或者確定有值的對(duì)象來調(diào)用equals
- "test".equals(Object)
- 推薦使用java.util.objects
-
所有相同類型的包裝類對(duì)象之間的值的比較,全部使用equals方法比較
- 對(duì)于 Integer var = ? 在-128至127范圍內(nèi)賦值時(shí) ,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ì)象,所以推薦使用equals方法進(jìn)行比較
- 任何貨幣金額,均以最小貨幣單位且整型類型來進(jìn)行存儲(chǔ)
-
浮點(diǎn)數(shù)之間的等值判斷:
- 基本類型不能用 == 來比較
- 包裝類型不能使用equals來判斷
- 浮點(diǎn)數(shù)采用尾數(shù)+階碼的編碼方式,類似與科學(xué)計(jì)數(shù)法有效數(shù)字+指數(shù)的表示方式. 二進(jìn)制無法精確表示大部分十進(jìn)制小數(shù)
- 為了避免出現(xiàn)問題,所有的浮點(diǎn)數(shù)都使用BigDecimal定義
/*
* float類型的浮點(diǎn)數(shù):
* 指定一個(gè)誤差范圍,
* 兩個(gè)浮點(diǎn)數(shù)的差值在此范圍之內(nèi),
* 則認(rèn)為是相等的.
*/
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
float diff = 1e - 6f;
if (Math.abs(a-b) < diff) {
System.out.println("true");
}
/*
* 使用BigDecimal來定義值,再進(jìn)行浮點(diǎn)數(shù)的運(yùn)算操作
*/
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.substract(b);
BigDecimal y = b.substract(c);
if (x.equals(y)) {
System.out.println("true");
}
- 定義數(shù)據(jù)對(duì)象DO類時(shí),屬性類型要與數(shù)據(jù)庫字段類型相匹配
- 數(shù)據(jù)庫字段的bigint必須與類屬性Long類型相對(duì)應(yīng)
- 禁止使用構(gòu)造方法BigDecimal(double) 的方式將double值轉(zhuǎn)化為BigDecimal對(duì)象:
- BigDecimal(double)存在精度損失風(fēng)險(xiǎn),在精確計(jì)算或值比較的場(chǎng)景中會(huì)導(dǎo)致業(yè)務(wù)邏輯異常
- 推薦使用入?yún)?strong>String的構(gòu)造方法
- 或者使用BigDecimal的valueOf方法: 此方法內(nèi)部執(zhí)行了Double的toString,實(shí)際能表達(dá)的精度對(duì)尾數(shù)進(jìn)行了截?cái)?/li>
- BigDecimal(double)存在精度損失風(fēng)險(xiǎn),在精確計(jì)算或值比較的場(chǎng)景中會(huì)導(dǎo)致業(yè)務(wù)邏輯異常
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = BigDecimal.valueOf(0.1);
-
基本類型和包裝類型的使用標(biāo)準(zhǔn):
- 所有的POJO類屬性必須使用包裝類數(shù)據(jù)類型
- RPC方法的返回值和參數(shù)必須使用包裝數(shù)據(jù)類型
- 所有的局部變量使用基本數(shù)據(jù)類型
- 定義DO,DTO,VO等POJO類時(shí),不要設(shè)定任何屬性默認(rèn)值
- 序列化類新增屬性時(shí),不能修改serialVersionUID字段,這樣會(huì)導(dǎo)致反序列化失敗;如果完全不兼容升級(jí),避免反序列化混亂,可以修改serialVersionUID值.在serialVersionUID不一致時(shí)會(huì)拋出序列化運(yùn)行時(shí)異常
- 構(gòu)造方法中禁止加入任何業(yè)務(wù)邏輯,如果有初始化邏輯,要放在init中
-
POJO類必須寫toString方法.如果繼承了一個(gè)POJO類,需要在前面添加super.toString
- 這樣在方法執(zhí)行拋出異常時(shí),可以直接調(diào)用POJO的toString()方法打印屬性值,便于排查問題
- 禁止在POJO類中,同時(shí)存在對(duì)應(yīng)屬性Xxx的isXxx() 和getXxx() 方法
- 框架在調(diào)用屬性Xxx的獲取方法時(shí),不能確定哪個(gè)方法一定是被優(yōu)先調(diào)用到的
- 使用索引訪問用String的split方法得到的數(shù)組時(shí),需要做最后一個(gè)分隔符后有無內(nèi)容的檢查, 否則會(huì)有IndexOutofBoundsException異常
- 當(dāng)一個(gè)類有多個(gè)構(gòu)造方法,或者多個(gè)同名方法,這些方法應(yīng)該按順序放置在一起,便于閱讀
- 類內(nèi)方法定義的順序依次為:
-
公有方法或者保護(hù)方法
- 公有方法是類調(diào)用者或者維護(hù)最頻繁使用的方法,最好首先展示
- 保護(hù)方法盡管是子類需要的方法,但也可能是模板設(shè)計(jì)模式中的核心方法
-
私有方法
- 私有方法外部一般不需要關(guān)心,是一個(gè)黑盒實(shí)現(xiàn)
-
getter或者setter方法
- 所有Service和DAO的getter或者setter方法都放在類的最后
-
公有方法或者保護(hù)方法
- setter方法中,參數(shù)名稱要和類成員變量名稱一致 ,this.成員名=參數(shù)名.
- 在getter或者setter方法中,不要增加業(yè)務(wù)邏輯
- 循環(huán)體內(nèi),字符串的類連接方式,使用StringBuilder的append方法進(jìn)行擴(kuò)展
- 否則會(huì)導(dǎo)致每次循環(huán)都會(huì)new一個(gè)新的StringBuilder對(duì)象
- 然后再進(jìn)行append操作
- 最后通過toString方法返回String對(duì)象,造成資源浪費(fèi)
- final可以聲明類,成員變量,方法,以及本地變量. 使用final的情況:
-
不允許被繼承的類
- String
- 不允許修改的引用的域?qū)ο?/strong>
-
不允許被重寫的方法
- POJO中的setter方法
- 不允許運(yùn)行過程中重新賦值的局部變量
- 避免上下文重復(fù)使用一個(gè)變量,使用final描述可以強(qiáng)制重新定義,方便更好地進(jìn)行重構(gòu)
-
不允許被繼承的類
- 不要使用Object的clone方法拷貝對(duì)象:
- 對(duì)象的clone方法默認(rèn)是淺拷貝
- 若想實(shí)現(xiàn)深度拷貝需要重寫clone方法實(shí)現(xiàn)域?qū)ο蟮纳疃缺闅v拷貝需要重寫clone方法實(shí)現(xiàn)域?qū)ο蟮纳疃缺闅v拷貝
-
類成員與方法訪問控制規(guī)約:
- 如果不允許外部直接通過new來創(chuàng)建對(duì)象,那么構(gòu)造方法必須是private
- 工具類不允許有public或者default構(gòu)造方法
- 類非static成員變量并且與子成員共享,必須是protected
- 類非static成員變量并且僅在本類中使用,必須是private
- 類static成員變量如果僅在本類中使用,必須是private
- 若是static成員變量,考慮是否為final
- 類成員方法只供類內(nèi)部調(diào)用時(shí),必須是private
- 類成員方法只對(duì)繼承類公開時(shí),限制使用protected
日期時(shí)間
- 日期格式化時(shí),傳入pattern中表示年份統(tǒng)一使用小寫的yyyy
- 日期格式化時(shí):
- yyyy表示當(dāng)天所在的年
- YYYY表示當(dāng)天所在的周屬于的年份,一周從周日開始,至周六結(jié)束.如果本周跨年,返回的YYYY就是下一年
- 日期格式化時(shí):
- 在日期格式中分清楚大寫的M和小寫的m,大寫的H和小寫的h的含義:
- 表示月份的是大寫的M
- 表示分鐘的是小寫的m
- 24小時(shí)的是大寫的H
- 12小時(shí)的是小寫的h
- 獲取當(dāng)前的毫秒數(shù) :System.currentTimeMillis()
- 如果想要獲取更加精確的納秒級(jí)的時(shí)間值,使用System.nanoTime()
- 針對(duì)統(tǒng)計(jì)時(shí)間的場(chǎng)景,推薦使用Instant
- 不要使用java.sql中的相關(guān)時(shí)間方法
- 不要在程序中寫死一年的為365,避免在公歷閏年時(shí)出現(xiàn)日期轉(zhuǎn)換錯(cuò)誤或程序邏輯錯(cuò)誤
- 使用LocalDate方法
// 獲取今年的天數(shù)
int daysOfThisYear = LocalDate.now().lengthOfYear();
// 獲取指定某年的天數(shù)
LocalDate.of(2011, 1, 1).lengthOfYear();
-
使用Calendar中的枚舉值來指代月份
- 如果使用數(shù)字,要注意Date,Calendar等日期相關(guān)類的月份month的值在0 - 11之間
集合處理
-
hashCode和equals的處理:
- 只要重寫equals, 就必須重寫hashCode
- Set中存儲(chǔ)的是不重復(fù)的對(duì)象,依據(jù)hashCode和equals進(jìn)行判斷,所以Set存儲(chǔ)的對(duì)象必須重寫這兩個(gè)方法
- 如果自定義對(duì)象作為Map的鍵,必須重寫hashCode和equals
- String重寫了hashCode和equals方法所以可以使用String對(duì)象作為key來使用
-
ArrayList的subList結(jié)果不可以強(qiáng)轉(zhuǎn)成ArrayList,否則會(huì)拋出ClassCastException異常:
- subList返回的是ArrayList的內(nèi)部類SubList, 并不是ArrayList, 而是ArrayList的一個(gè)視圖.對(duì)于SubList子列表的所有操作最終會(huì)反映到原列表上
- 在subList場(chǎng)景中,要注意對(duì)原集合元素的增加或者刪除,都會(huì)導(dǎo)致子列表的遍歷,增加和刪除產(chǎn)生ConcurrentModificationException異常
- 使用Map的方法:
- keySet()
- values()
- entrySet()
- 返回集合對(duì)象時(shí),不可以進(jìn)行添加元素的操作,否則會(huì)拋出UnsupportedOperationException異常
-
Collections類返回的對(duì)象不可以進(jìn)行添加或者刪除操作:
- 如果查詢無結(jié)果,則返回Collection.emptyList()空集合對(duì)象. 調(diào)用方一旦進(jìn)行了添加元素操作,就會(huì)觸發(fā)UnsupportedOperationException異常
- 使用集合轉(zhuǎn)數(shù)組的方法,必須使用集合的 toArrary(T[] array), 傳入的是類型完全一樣的數(shù)組,數(shù)組的大小就是list.size()
- 使用toArray帶參方法,入?yún)⒎峙涞臄?shù)組空間不夠大時(shí),toArray方法內(nèi)部將重新分配內(nèi)存空間,并返回新數(shù)組的地址;
- 如果數(shù)組元素個(gè)數(shù)大于實(shí)際所需,下標(biāo)為[list.size()] 的元素的數(shù)組元素將被置為null,其余數(shù)組元素保持原值
- 因此最好將方法入?yún)?shù)組大小定義為與集合元素個(gè)數(shù)一致
List<String> list = new ArrayList<>();
list.add("guan");
list.add("bao");
String[] array = new String[list.size];
array = list.toArray(array);
- 在使用Collection接口任何實(shí)現(xiàn)類的addAll() 方法時(shí),都要對(duì)輸入集合參數(shù)進(jìn)行NPE判斷
- 使用工具類Arrays.asList()將數(shù)組轉(zhuǎn)換成集合時(shí),不能使用這個(gè)相關(guān)的修改集合的方法,這個(gè)集合的add, remove, clear方法會(huì)拋出UnsupportedOperationException異常
- asList的返回對(duì)象是一個(gè)Arrays內(nèi)部類,并沒有實(shí)現(xiàn)集合的修改方法
- Arrays.asList體現(xiàn)的是適配器模式,只是轉(zhuǎn)換接口,后臺(tái)數(shù)據(jù)依舊是數(shù)組
- 泛型通配符 <? extends T> 來接收返回的數(shù)據(jù),這種寫法的泛型集合不能使用add方法 ;<? super T> 不能使用get方法,作為接口調(diào)用賦值時(shí)會(huì)出錯(cuò)
-
PECS(Producer Extends Consumer Super)原則:
- 頻繁往外讀取內(nèi)容,適合使用<? extends T>
- 經(jīng)常往里插入的,適合使用<? super T>
-
PECS(Producer Extends Consumer Super)原則:
- 不要在foreach循環(huán)里進(jìn)行元素的remove或者add操作
- remove元素要使用Iterator方式,如果是并發(fā)操作,要對(duì)Iterator對(duì)象加鎖
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (condition) {
iterator.remove();
}
}
- 在JDK 7以后的版本中 ,Comparator實(shí)現(xiàn)要滿足三個(gè)條件,否則Arrays.sort, Collections.sort會(huì)出現(xiàn)IllegalArgumentException異常:
- x, y的比較結(jié)果和y, x的比較結(jié)果相反
- x > y, y > z, 則 x > z
- x = y, 則x, z比較結(jié)果和y, z比較結(jié)果相同
- 在JDK 7以后的版本中,給集合的泛型定義時(shí),使用全省略,即直接使用 <> 來指定前邊已經(jīng)指定的類型
-
集合初始化時(shí),指定集合初始值大小
- HashMap使用HashMap(int initialCapacity) 初始化
- initalCapacity = (需要存儲(chǔ)的元素個(gè)數(shù) / 負(fù)載因子) + 1. 注意負(fù)載因子(即loader factor)默認(rèn)為0.75,如果暫時(shí)無法確定初始值的大小,設(shè)為為默認(rèn)值16
- 使用entrySet遍歷Map類集合kv, 而不是使用keySet方式進(jìn)行遍歷
- 如果使用keySet方式遍歷,其實(shí)是遍歷了兩次:
- 一次轉(zhuǎn)換為Iterator對(duì)象
- 一次從hashMap中取出key所對(duì)應(yīng)的value
- entrySet只是遍歷一次就把key和value都放到了entry中,效率更高
- 如果是JDK 8以后的版本,使用Map.foreach方法
-
示例:
- values()返回的是V值集合,是一個(gè)list集合對(duì)象
- keySet()返回的是K值集合,是一個(gè)Set集合對(duì)象
- entrySet()返回的是K-V值組合集合
- 如果使用keySet方式遍歷,其實(shí)是遍歷了兩次:
- 要注意Map類集合中的K-V能不能存儲(chǔ)null值的情況:
| 集合類 | Key | Value | Super | 說明 |
|---|---|---|---|---|
| Hashtable | 不允許為null | 不允許為null | Dictionary | 線程安全 |
| ConcurrentHashMap | 不允許為null | 不允許為null | AbstractMap | 鎖分段技術(shù) |
| TreeMap | 不允許為null | 允許為null | AbstractMap | 線程不安全 |
| HashMap | 允許為null | 允許為null | AbstractMap | 線程不安全 |
由于HashMap的干擾,誤以為ConcurrentHashMap可以置入null值,其實(shí)這樣會(huì)拋出NPE異常
- 合理利用集合的有序型 - sort和集合的穩(wěn)定性 - order, 避免集合的無序性 - unsort和不穩(wěn)定性 - unorder帶來的負(fù)面影響
- 有序性是指遍歷的結(jié)果按照某種比較規(guī)則依次排列的
- 穩(wěn)定性是指集合每次遍歷的元素次序是一定的
- ArrayList, HashMap, TreeSet
-
利用Set元素唯一的特性,可以快速對(duì)一個(gè)集合進(jìn)行去重操作
- 避免使用List的contains方法進(jìn)行遍歷,對(duì)比,去重操作
并發(fā)處理
-
獲取單例對(duì)象需要保證線程安全,其中的方法也要保證線程安全
- 資源驅(qū)動(dòng)類, 工具類, 單例工廠類都需要注意
- 創(chuàng)建線程或者線程池時(shí)要指定有意義的線程名稱,方便出錯(cuò)時(shí)回溯
-
線程資源必須通過線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程
- 使用線程池的好處是減少在創(chuàng)建和銷毀線程上所消耗的時(shí)間以及系統(tǒng)資源的開銷,解決資源不足的問題
- 如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致消耗完內(nèi)存或者過度切換的問題
- 線程池不允許使用Executors創(chuàng)建,要通過ThreadPoolExecutors創(chuàng)建,這樣可以讓人更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源消耗的風(fēng)險(xiǎn)
- Executors返回線程池對(duì)象存在以下問題:
-
FixedThreadPool和SingleThreadPool:
- 允許請(qǐng)求隊(duì)列長度為Integer.MAX_VALUE,可能會(huì)堆積大量請(qǐng)求,導(dǎo)致OOM
-
CachedThreadPool和ScheduledThreadPool:
- 允許創(chuàng)建的線程數(shù)量為Integer.MAX_VALUE,可能會(huì)創(chuàng)建大量線程,導(dǎo)致OOM
-
FixedThreadPool和SingleThreadPool:
- Executors返回線程池對(duì)象存在以下問題:
-
SimpleDateFormat是線程不安全類,不要定義為static變量.如果定義為static,必須加鎖,或者使用DateUtils工具類
- 注意線程安全,使用DateUtils,可以進(jìn)行如下處理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }- 在JDK 8中,可以使用:
- Instant 代替 Date
- LocalDateTime 代替 Calendar
- DateTimeFormatter 代替 SimpleDateFormat
-
必須回收自定義的ThreadLocal變量:
- 尤其在線程池場(chǎng)景下,線程經(jīng)常會(huì)被復(fù)用,如果不清理自定義的ThreadLocal變量,會(huì)影響后續(xù)業(yè)務(wù)邏輯和造成內(nèi)存泄漏的問題
- 盡量在代理中使用try - finally塊進(jìn)行回收
ObjectThreadLocal.set(userInfo);
try {
...
} finally {
ObjectThreadLocal.remove();
}
-
高并發(fā)時(shí),同步調(diào)用應(yīng)該考量鎖的性能損耗.
- 能用無鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖
- 能用鎖區(qū)塊,就不要鎖整個(gè)方法體
- 能用對(duì)象鎖,就不要用類鎖
- 盡可能使加鎖的代碼塊工作量盡可能的小,避免在鎖代碼塊中調(diào)用RPC方法
-
對(duì)多個(gè)資源, 數(shù)據(jù)庫表, 對(duì)象同時(shí)加鎖時(shí),需要保持一致的加鎖順序,否則可能會(huì)造成死鎖
- 如果線程一需要對(duì)A, B, C依次全部加鎖后才可以進(jìn)行更新操作
- 那么線程二的加鎖順序也必須是A, B, C.否則可能會(huì)出現(xiàn)死鎖
-
在使用阻塞等待獲取鎖的方式中:
-
必須在try代碼塊之外
- 如果lock方法在try代碼塊之內(nèi),可能由于其它方法拋出異常 ,導(dǎo)致在finally代碼塊中 ,unlock對(duì)未加鎖的對(duì)象解鎖,會(huì)調(diào)用AQS的tryRelease方法,拋出IlleagalMonitorStateException異常
-
必須在加鎖方法與try代碼塊之間沒有任何可能拋出異常的方法調(diào)用,避免加鎖成功后,在finally中無法解鎖
- 如果在lock方法與try代碼塊之間的方法調(diào)用拋出異常,那么無法解鎖,造成其它線程無法獲取鎖
- 在Lock對(duì)象的lock方法實(shí)現(xiàn)中可能拋出unchecked異常,導(dǎo)致unlock對(duì)未加鎖的對(duì)象解鎖,會(huì)調(diào)用AQS的tryRelease方法,拋出IlleagalMonitorStateException異常
-
必須在try代碼塊之外
Lock lock = new XxxLock();
lock.lock();
try {
...
} finally {
lock.unlock();
}
-
在使用嘗試機(jī)制來獲取鎖的方式中:
- 進(jìn)入業(yè)務(wù)代碼之前,必須先判斷當(dāng)前線程是否持有鎖
- 鎖的釋放規(guī)則與鎖阻塞等待的方式相同
- Lock對(duì)象的unlock方法在執(zhí)行時(shí),會(huì)調(diào)用AQS的tryRelease方法,如果當(dāng)前線程不持有鎖, 則拋出IllegalMonitorStateException異常
Lock lock = new XxxLock();
boolean isLocked = lock.tryLock();
if (isLocked) {
try {
...
} finally {
lock.unlock();
}
}
-
并發(fā)修改同一記錄時(shí),避免更新丟失,需要加鎖:
- 在應(yīng)用層加鎖
- 在緩存加鎖
- 在數(shù)據(jù)庫中加鎖
-
使用version作為更新依據(jù)
- 如果每次訪問概率小于20%, 推薦使用樂觀鎖
- 否則的話,使用悲觀鎖
- 樂觀鎖的重試次數(shù)不得小于3次
-
多線程并行處理定時(shí)任務(wù)時(shí):
- Timer運(yùn)行多個(gè)TimerTask時(shí)只要其中之一沒有捕獲拋出的異常,任務(wù)便會(huì)自動(dòng)終止運(yùn)行
- 使用ScheduleExecutorService則沒有這個(gè)問題
- 悲觀鎖遵循一鎖二判三更新四釋放的原則
- 使用CountDownLatch進(jìn)行異步轉(zhuǎn)同步操作:
- 每個(gè)線程退出前必須調(diào)用countDown方法
- 線程執(zhí)行代碼注意catch異常,確保counDown方法被執(zhí)行到
-
避免主線程無法執(zhí)行至await方法,直到超時(shí)才返回結(jié)果
- 子線程拋出的異常堆棧,不能在主線程try-catch得到異常
- 避免Random實(shí)例被多線程使用,共享該實(shí)例是線程安全的,但是會(huì)因?yàn)楦?jìng)爭(zhēng)同一個(gè)seed導(dǎo)致性能下降
-
Random實(shí)例:
- java.util.Random的實(shí)例
- Math.random() 的方式
- 在JDK 7后,可以直接使用ThreadLoalRandom
-
Random實(shí)例:
- 在并發(fā)的場(chǎng)景下,通過雙重檢查鎖double-check locking實(shí)現(xiàn)延遲初始化來優(yōu)化問題隱患:
- 將目標(biāo)屬性聲明為volatile型
-
volatile用于解決多線程內(nèi)存不可見問題:
- 對(duì)于一寫多讀,可以解決變量同步問題
- 對(duì)于多寫,無法解決線程安全問題
- 對(duì)于count++操作,使用如下的類實(shí)現(xiàn):
AtomicInteger count = new AtomicInteger(); count.addAndGet(1);- 在JDK 8后,推薦使用LongAdder對(duì)象,比AtomicLong性能更好,因?yàn)榭梢詼p少樂觀鎖的重試次數(shù)
- HashMap在容量不夠進(jìn)行resize操作時(shí)會(huì)由于高并發(fā)可能出現(xiàn)死鎖,導(dǎo)致CPU增加:
- 使用其它的數(shù)據(jù)結(jié)構(gòu)
- 加鎖
-
ThreadLocal無法解決共享對(duì)象的更新問題,建議要使用static進(jìn)行修飾:
- 這個(gè)變量是針對(duì)一個(gè)線程內(nèi)所有操作共享的
- 因此設(shè)置為靜態(tài)變量,所有的此類實(shí)例共享此靜態(tài)變量
- 即這個(gè)變量在類第一次被使用時(shí)裝載,只分配一塊內(nèi)存空間,只要這個(gè)線程內(nèi)定義的所有此類的對(duì)象都可以操作這個(gè)變量
控制語句
-
在一個(gè)switch塊內(nèi):
- 每個(gè)case要通過break或者return來終止
- 或者注釋說明程序?qū)⒗^續(xù)執(zhí)行到哪一個(gè)case為止
- 必須包含一個(gè)default語句并且放在最后,即使是空代碼
- 當(dāng)Switch括號(hào)內(nèi)的變量類型為String并且此變量為外部參數(shù)時(shí),必須進(jìn)行null判斷
- 在 if, else, for, while, do語句中必須使用大括號(hào),即使只有一行代碼,避免采用單行編碼模式
- 三目運(yùn)算符: condition ? 表達(dá)式1 : 表達(dá)式2 要注意表達(dá)式1和表達(dá)式2在類型對(duì)齊時(shí),可能因自動(dòng)拆箱導(dǎo)致NPE異常
- 觸發(fā)類型對(duì)齊的拆箱操作:
- 表達(dá)式1或者表達(dá)式2只要有一個(gè)原始類型
- 表達(dá)式1或者表達(dá)式2類型不一致,會(huì)強(qiáng)制拆箱升級(jí)成表示范圍更大的那個(gè)類型
- 觸發(fā)類型對(duì)齊的拆箱操作:
- 在高并發(fā)的場(chǎng)景中,避免使用 “等于” 判斷作為中斷或者退出的條件
- 因?yàn)槿绻l(fā)控制沒有處理好,容易產(chǎn)生等值判斷被 “擊穿” 的情況 .要使用大于或者小于區(qū)間判斷條件來代替
- 示例: 判斷剩余數(shù)量等于0時(shí),當(dāng)數(shù)量等于0的過程中,由于并發(fā)處理錯(cuò)誤導(dǎo)致數(shù)量瞬間變成了負(fù)數(shù),這樣的話,處理無法終止
- 表達(dá)異常的分支時(shí),不要使用if - else方式,改寫為
if (condition) {
...
return obj;
}
// 然后寫else的業(yè)務(wù)處理邏輯
對(duì)于超過3層的if - else的邏輯判斷代碼可以使用衛(wèi)語句,策略模式,狀態(tài)模式等實(shí)現(xiàn)
- 除常用的方法 :getXxx, isXxx等,不要在條件判斷中執(zhí)行復(fù)雜的語句,將復(fù)雜邏輯判斷的結(jié)果賦值給一個(gè)有意義的布爾變量名,以提高可讀性
- 很多if語句內(nèi)的邏輯相當(dāng)復(fù)雜,需要分析表達(dá)式的最終結(jié)果,才能明確什么樣的條件執(zhí)行什么樣的語句
- 不要在其它表達(dá)式中(尤其時(shí)條件表達(dá)式),插入賦值語句
- 循環(huán)體中的語句要考量性能,以下操作盡量移動(dòng)至循環(huán)體外處理:
- 定義對(duì)象,變量
- 獲取數(shù)據(jù)庫連接
- 進(jìn)行不必要的try - catch操作(考慮這個(gè)try - catch操作是否可以移動(dòng)至循環(huán)體外)
- 避免使用取反邏輯運(yùn)算符
- 取反邏輯運(yùn)算符不利于快速理解
- 取反邏輯寫法必然存在對(duì)應(yīng)的正向邏輯寫法
- 接口入?yún)⒈Wo(hù): 這種場(chǎng)景常見的是用作批量操作的接口
-
參數(shù)校驗(yàn):
-
需要進(jìn)行參數(shù)校驗(yàn)的情形:
- 調(diào)用頻次低的方法
- 執(zhí)行時(shí)間開銷很大的方法
- 此情形中,參數(shù)校驗(yàn)的時(shí)間幾乎可以忽略不計(jì)
- 但是如果因?yàn)閰?shù)錯(cuò)誤導(dǎo)致中間執(zhí)行被退回,或者錯(cuò)誤,就得不償失
- 需要極高穩(wěn)定性和可用性的方法
- 對(duì)外提供開放接口,無論是 RPC, API, HTTP接口
- 敏感權(quán)限入口
-
不需要進(jìn)行參數(shù)校驗(yàn)的情形:
- 極有可能被循環(huán)調(diào)用的方法. 但是在方法說明里必須注明外部參數(shù)的檢查要求
- 底層調(diào)用頻度比較高的方法
- 被聲明成private只會(huì)被自己代碼所調(diào)用的方法.如果能夠確定調(diào)用方法的代碼傳入?yún)?shù)已經(jīng)做過檢查或者肯定不會(huì)有問題,此時(shí)可以不校驗(yàn)參數(shù)
-
需要進(jìn)行參數(shù)校驗(yàn)的情形:
注釋規(guī)約
- 類, 類屬性, 類方法的注釋必須使用Javadoc規(guī)范,使用/** xxx */格式,不允許使用// xxx方式
- 所有抽象方法, 包括接口中的方法, 都必須使用Javadoc注釋,除了返回值, 參數(shù), 異常說明外,還必須指出該方法做了什么事情,實(shí)現(xiàn)什么功能. 對(duì)子類的實(shí)現(xiàn)要求以及調(diào)用的注意事項(xiàng)需要一并說明
- 所有的類都必須添加創(chuàng)建者和創(chuàng)建日期
-
方法內(nèi)部注釋:
- 單行注釋: 在被注釋語句上方另起一行,使用 // 注釋
- 多行注釋: 使用 /* */ 注釋,注意與代碼對(duì)齊
- 所有枚舉類型字段必須要有注釋,說明每個(gè)數(shù)據(jù)項(xiàng)的用途
- 當(dāng)水平足夠高時(shí),應(yīng)當(dāng)使用英文注釋. 否則就用中文把問題說清楚,只要將專有名詞與關(guān)鍵字保持英文原文即可
- 代碼修改的同時(shí),注釋也要進(jìn)行相應(yīng)的修改,尤其是參數(shù), 返回值, 異常, 核心邏輯等. 要保持代碼與注釋更新同步
-
謹(jǐn)慎注釋代碼:
- 注釋的代碼要進(jìn)行詳細(xì)的說明,而不是簡單的注釋
- 如果無用,則應(yīng)該刪除
-
注釋的要求:
- 能夠準(zhǔn)確反映設(shè)計(jì)思想和代碼邏輯
- 能夠描述業(yè)務(wù)含義,能夠迅速了解到代碼背后的信息
- 好的命名,代碼結(jié)構(gòu)是自解釋的,注釋保證精簡準(zhǔn)確,表達(dá)到位
- 特殊的注釋標(biāo)記,需要注明標(biāo)記人與標(biāo)記時(shí)間.注意及時(shí)處理這些標(biāo)記,通過標(biāo)記掃描,經(jīng)常清理此類標(biāo)記.線上故障有時(shí)候就源于這些標(biāo)記處的代碼
- 待辦事宜TODO : (標(biāo)記人, 標(biāo)記時(shí)間, [預(yù)處理時(shí)間])?
- 表示要實(shí)現(xiàn),但目前尚未實(shí)現(xiàn)的功能.這實(shí)際上是一個(gè)Javadoc的標(biāo)簽.只能應(yīng)用于類, 接口, 方法
- 錯(cuò)誤,不能工作FIXME : (標(biāo)記人, 標(biāo)記時(shí)間, [預(yù)處理時(shí)間])
- 在注釋中用FIXME標(biāo)記某段代碼是錯(cuò)誤的,而且不能工作,需要及時(shí)糾正情況
- 待辦事宜TODO : (標(biāo)記人, 標(biāo)記時(shí)間, [預(yù)處理時(shí)間])?
前后分離
- 前后端交互的API,需要明確協(xié)議,域名,路徑,請(qǐng)求方法,請(qǐng)求內(nèi)容,狀態(tài)碼,響應(yīng)體:
- 協(xié)議: 生產(chǎn)環(huán)境必須使用HTTPS
-
路徑: 每一個(gè)API需要對(duì)應(yīng)一個(gè)路徑,表示API具體的請(qǐng)求地址
- 代表資源,只能為名詞,推薦使用復(fù)數(shù),不能為動(dòng)詞,因?yàn)檎?qǐng)求方法已經(jīng)表達(dá)動(dòng)作含義
- URL路徑不能使用大寫,單詞如果需要分割,統(tǒng)一使用下劃線
- 路徑禁止攜帶請(qǐng)求內(nèi)容類型的后綴 : ".json",".xml", 通過accept頭表達(dá)即可
-
請(qǐng)求方法: 對(duì)具體操作的定義
- GET: 獲取
- POST: 新增
- PUT: 修改
- DELETE: 刪除
-
請(qǐng)求內(nèi)容:
- URL帶的參數(shù)必須無敏感信息或者符合安全要求
- body里帶參數(shù)時(shí)必須設(shè)置Content-Type
- 響應(yīng)體: 響應(yīng)體body可以放置多種數(shù)據(jù)類型,由Content-Type頭來確定
- 前后端數(shù)據(jù)列表相關(guān)的接口返回時(shí),如果為空,則返回空數(shù)組 [ ] 或者空集合 { }
- 服務(wù)端發(fā)生錯(cuò)誤時(shí),返回給前端的響應(yīng)信息必須包含HTTP狀態(tài)碼, errorCode, errorMessage, 用戶提示信息四個(gè)部分:
-
HTTP狀態(tài)碼: 瀏覽器
- 200 OK : 表明該請(qǐng)求被成功完成, 所請(qǐng)求的資源發(fā)送到客戶端
- 401 Unauthorized : 請(qǐng)求要求身份驗(yàn)證, 通常是需要登錄而用戶未登錄的情況
- 403 Forbidden : 服務(wù)器拒絕請(qǐng)求, 通常是機(jī)密信息或復(fù)制其余登錄用戶鏈接訪問服務(wù)器的情況
- 404 Not Found : 服務(wù)器無法取得所請(qǐng)求的網(wǎng)頁. 請(qǐng)求的資源不存在
- 500 Internal Server Error: 服務(wù)器內(nèi)部錯(cuò)誤
- errorCode: 前端開發(fā)
- errorMessage: 錯(cuò)誤排查人員
- 用戶提示信息: 用戶. 要求簡短清晰,提示友好,引導(dǎo)用戶進(jìn)行下一步操作或者解釋錯(cuò)誤原因,上下文環(huán)境,推薦操作
-
HTTP狀態(tài)碼: 瀏覽器
- errorMessage是前后端錯(cuò)誤追蹤機(jī)制的體現(xiàn),可以在前端輸出到 type="hidden" 的文字類控件或者用戶端的日志中,這樣能夠快速地定位問題
- 對(duì)于需要使用超大整數(shù)的場(chǎng)景,服務(wù)端一律使用String字符串返回類型,禁止使用Long類型
-
Java服務(wù)端如果直接返回Long整型數(shù)據(jù)給前端 ,JS會(huì)自動(dòng)轉(zhuǎn)換為Number類型:
- Number類型: 雙精度浮點(diǎn)數(shù),表示原理和取值范圍等同于Java中的Double
-
Long類型: 表示的最大值為263 -1. 超過253(9007199254740992) 的數(shù)值轉(zhuǎn)化為JS的Number時(shí),有些數(shù)值會(huì)有精度損失
- 在Long取值范圍內(nèi),任何2的指數(shù)次整數(shù)都是絕對(duì)不會(huì)存在精度損失的,所以說精度損失是一個(gè)概率問題
- 如果浮點(diǎn)數(shù)尾數(shù)位與指數(shù)位空間不限,則可以精確表示任何整數(shù).但是雙精度浮點(diǎn)數(shù)的尾數(shù)位只有52位
- 示例: 通常在訂單號(hào)或者交易號(hào)大于等于16位,大概率會(huì)出現(xiàn)前后端單據(jù)不一致的情況. 比如后端的362909601374617692到前端則是362909601374617660
-
Java服務(wù)端如果直接返回Long整型數(shù)據(jù)給前端 ,JS會(huì)自動(dòng)轉(zhuǎn)換為Number類型:
-
HTTP請(qǐng)求通過URL傳遞參數(shù)時(shí),不能超過2048個(gè)字節(jié):
- 不同瀏覽器對(duì)于URL的最大長度限制略有不同,并且對(duì)超出最大長度的處理邏輯也有差異. 2048字節(jié)是取所有瀏覽器的最小值
-
HTTP請(qǐng)求通過body傳遞內(nèi)容時(shí),必須控制長度,超出最大長度后,后端解析會(huì)出錯(cuò):
- Nginx默認(rèn)限制是1MB
- Tomcat默認(rèn)限制是2MB
- 當(dāng)確實(shí)有業(yè)務(wù)需要傳較大內(nèi)容時(shí),可以通過調(diào)大服務(wù)器端的限制
- 在分頁場(chǎng)景中,用戶輸入?yún)?shù)小于1, 則前端返回第一頁參數(shù)給后端. 后端發(fā)現(xiàn)用戶輸入的參數(shù)大于總頁數(shù),直接返回最后一頁
- 服務(wù)器內(nèi)部重定向必須使用forward. 外部重定向地址必須使用URL統(tǒng)一代理模塊生成,否則會(huì)因?yàn)榫€上采用HTTPS協(xié)議而導(dǎo)致瀏覽器提示 "不安全", 并且還會(huì)帶來URL維護(hù)不一致的問題
- 服務(wù)器返回信息必須被標(biāo)記是否可以緩存,如果緩存,客戶端可能會(huì)重用之前的請(qǐng)求結(jié)果
- 緩存有利于減少交互次數(shù),減少交互的平均延遲
-
示例: http 1.1中 ,s-maxage通知服務(wù)器進(jìn)行緩存,時(shí)間單位為秒:
- response.setHeader("Cache-Control", "s-maxage=" + cacheSeconds)
- 服務(wù)端返回的數(shù)據(jù),使用JSON格式而非XML :
- HTTP支持使用不同的輸出格式,例如純文本,JSON,CSV,XML,RSS以至HTML
- 在使用面向用戶的服務(wù),應(yīng)該選擇JSON作為通信中使用的標(biāo)準(zhǔn)數(shù)據(jù)交換格式,包括請(qǐng)求和響應(yīng)
- application/JSON是一種通用的MIME類型,具有實(shí)用,精簡,易讀的特點(diǎn)
- 前后端的時(shí)間格式統(tǒng)一為 "yyyy-MM-dd HH:mm:ss", 統(tǒng)一為GMT
其它注意
- 在使用正則表達(dá)式時(shí), 利用好預(yù)編譯功能,可以有效加快正則匹配速度
- 不要在方法體內(nèi)定義
- 二方庫中可以定義枚舉類型,參數(shù)可以使用枚舉類型,但是接口返回值不允許使用枚舉類型或者包含枚舉類型的POJO對(duì)象
- velocity調(diào)用POJO類的屬性時(shí),直接使用屬性名取值即可,模板引擎會(huì)自動(dòng)按規(guī)范調(diào)用POJO的getXxx(), 如果是boolean基本類型變量 ,boolean命名不要加is前綴, 會(huì)自動(dòng)調(diào)用isXxx方法.如果是Boolean包裝類對(duì)象,優(yōu)先調(diào)用getXxx() 方法
- 后臺(tái)輸送給頁面變量必須加上 $ ! {var},注意中間的感嘆號(hào)
- 如果var等于null或者不存在,那么${var}會(huì)直接顯示在桌面上
- 注意Math.random() 這個(gè)方法返回是double類型,取值范圍0 <= x <1(能夠取到零值,注意除零)
- 如果獲取整數(shù)類型的隨機(jī)數(shù),不需要將x放大10的若干倍然后取整,直接使用Random對(duì)象的nextInt或者nextLong方法
- 獲取當(dāng)前秒數(shù)System.currentTimeMillis(), 不是使用new Date().getTime()
- 如果想獲取更加精確的納秒級(jí)時(shí)間值,使用System.nanoTime() 的方式
- 在JDK 8以后,針對(duì)統(tǒng)計(jì)時(shí)間等常景,需要使用Instant
- 不要在視圖模版中加入任何復(fù)雜邏輯,根據(jù)MVC理論,視圖的職責(zé)是展示,不要有模型和控制器的代碼邏輯
- 任何數(shù)據(jù)結(jié)構(gòu)的構(gòu)造和初始化,都應(yīng)指定大小,避免數(shù)據(jù)結(jié)構(gòu)無限增長吃光內(nèi)存
- 及時(shí)清理不再使用的代碼段或配置信息
- 對(duì)于垃圾代碼或過時(shí)配置,堅(jiān)決清理干凈,避免程序過度臃腫,代碼冗余
- 對(duì)于暫時(shí)被注釋掉,后續(xù)可能恢復(fù)使用的代碼片段,在注釋代碼的上方,統(tǒng)一規(guī)定使用三個(gè)斜杠/// 來說明注視掉代碼的理由