阿里巴巴Java開發手冊(詳盡版)-我的未注意到的知識點(轉)

轉自 https://blog.csdn.net/u013039395/article/details/86528164html

1、編程規約

(一) 命名風格

【強制】代碼中的命名只可用英文方式

【強制】類名使用UpperCamelCase風格,

但如下例外:(分層領域模型規約2)
- DO( Data Object):數據對象,與數據庫表結構一一對應,經過DAO層向上傳輸數據源對象。
- DTO( DataTransfer Object):數據傳輸對象,Service或Manager向外傳輸的對象。
- BO( BusinessObject):業務對象。 由Service層輸出的封裝業務邏輯的對象
- AO( Application Object):應用對象。在Web層與Service層之間抽象的複用對象模型,極爲貼近展現層,複用度不高。
- VO( View Object):顯示層對象或展現對象,一般是Web向模板渲染引擎層傳輸的對象
- POJO( Plain Ordinary Java Object):在本手冊中, POJO專指只有setter/getter/toString的簡單類,包括DO/DTO/BO/VO等。
- Query:數據查詢對象,各層接收上層的查詢請求。 注意超過2個參數的查詢封裝,禁止使用Map類來傳輸。java

【強制】POJO類中布爾類型的變量,都不要加is前綴,不然部分框架解析會引發序列化錯誤

反例:定義爲基本數據類型Boolean isDeleted的屬性,它的方法也是isDeleted(),RPC框架在反向解析的時候,「誤覺得」對應的屬性名稱是deleted,致使屬性獲取不到,進而拋出異常。git

【強制】包名統一使用小寫,點分隔符之間有且僅有一個天然語義的英語單詞。包名統一使用單數形式,可是類名若是有複數含義,類名可使用複數形式。

正例:應用工具類包名爲com.alibaba.ai.util、類名爲MessageUtils(此規則參考spring的框架結構)程序員

【推薦】接口類中的方法和屬性不要加任何修飾符號(public 也不要加),保持代碼的簡潔性,並加上有效的Javadoc註釋。儘可能不要在接口裏定義變量,若是必定要定義變量,確定是與接口方法相關,而且是整個應用的基礎常量。

說明:JDK8中接口容許有默認實現,那麼這個default方法,是對全部實現類都有價值的默認實現。
正例:接口方法簽名void commit();
接口基礎常量String COMPANY = 「alibaba」;
反例:接口方法定義public abstract void f();github

【參考】各層命名規約:

A) Service/DAO層方法命名規約
1) 獲取單個對象的方法用get作前綴。
2) 獲取多個對象的方法用list作前綴,複數形式結尾如:listObjects。
3) 獲取統計值的方法用count作前綴。
4) 插入的方法用save/insert作前綴。
5) 刪除的方法用remove/delete作前綴。
6) 修改的方法用update作前綴。
B) 領域模型命名規約
1) 數據對象:xxxDO,xxx即爲數據表名。
2) 數據傳輸對象:xxxDTO,xxx爲業務領域相關的名稱。
3) 展現對象:xxxVO,xxx通常爲網頁名稱。
4) POJO是DO/DTO/BO/VO的統稱,禁止命名成xxxPOJO。正則表達式

(二)常量定義

【強制】不容許任何魔法值(即未經預先定義的常量)直接出如今代碼中

【強制】在long或者Long賦值時,數值後使用大寫的L,不能是小寫的l,小寫容易跟數字1混淆,形成誤解。

【推薦】不要使用一個常量類維護全部常量,要按常量功能進行歸類,分開維護。

說明:大而全的常量類,雜亂無章,使用查找功能才能定位到修改的常量,不利於理解和維護。spring

【推薦】若是變量值僅在一個固定範圍內變化用enum類型來定義。

(三) 代碼格式

【強制】註釋的雙斜線與註釋內容之間有且僅有一個空格

【強制】IDE的text file encoding設置爲UTF-8; IDE中文件的換行符使用Unix格式,不要使用Windows格式。

(四) OOP規約

【強制】相同參數類型,相同業務含義,纔可使用Java的可變參數,避免使用Object。

說明:可變參數必須放置在參數列表的最後。(提倡同窗們儘可能不用可變參數編程)數據庫

【強制】外部正在調用或者二方庫依賴的接口,不容許修改方法簽名,避免對接口調用方產生

影響。接口過期必須加@Deprecated註解,並清晰地說明採用的新接口或者新服務是什麼。編程

【強制】不能使用過期的類或方法。

【強制】Object的equals方法容易拋空指針異常,應使用常量或肯定有值的對象來調用equals。

正例:「test」.equals(object);
反例:object.equals(「test」);
說明:推薦使用java.util.Objects#equals(JDK7引入的工具類)數組

【強制】全部的相同類型的包裝類對象之間值的比較,所有使用equals方法比較。

說明:對於Integer var = ? 在-128至127範圍內的賦值,Integer對象是在IntegerCache.cache產生,會複用已有對象,這個區間內的Integer值能夠直接使用==進行判斷,可是這個區間以外的全部數據,都會在堆上產生,並不會複用已有對象,這是一個大坑,
推薦使用equals方法進行判斷。

關於基本數據類型與包裝數據類型的使用標準以下:

1) 【強制】全部的POJO類屬性必須使用包裝數據類型。
2) 【強制】RPC方法的返回值和參數必須使用包裝數據類型。
3) 【推薦】全部的局部變量使用基本數據類型。
說明:POJO類屬性沒有初值是提醒使用者在須要使用時,必須本身顯式地進行賦值,任何
NPE問題,或者入庫檢查,都由使用者來保證。

【強制】構造方法裏面禁止加入任何業務邏輯,若是有初始化邏輯,請放在init方法中。

【強制】禁止在POJO類中,同時存在對應屬性xxx的isXxx()和getXxx()方法。

說明:框架在調用屬性xxx的提取方法時,並不能肯定哪一個方法必定是被優先調用到。

【推薦】使用索引訪問用String的split方法獲得的數組時,需作最後一個分隔符後有無內容的檢查,不然會有拋IndexOutOfBoundsException的風險。

說明:
String str = 「a,b,c,」;
String[] ary = str.split(",");
// 預期大於3,結果是3
System.out.println(ary.length);

【推薦】循環體內,字符串的鏈接方式,使用StringBuilder的append方法進行擴展。

說明:下例中,反編譯出的字節碼文件顯示每次循環都會new出一個StringBuilder對象,
而後進行append操做,最後經過toString方法返回String對象,形成內存資源浪費。
反例:
String str = 「start」;
for (int i = 0; i < 100; i++) {
str = str + 「hello」;
}

【推薦】慎用Object的clone方法來拷貝對象。

說明:對象的clone方法默認是淺拷貝,若想實現深拷貝須要重寫clone方法實現域對象的
深度遍歷式拷貝。

(五) 集合處理

【強制】關於hashCode和equals的處理,遵循以下規則:

1.只要重寫equals,就必須重寫hashCode。
2.由於Set存儲的是不重複的對象,依據hashCode和equals進行判斷,因此Set存儲的 對象必須重寫這兩個方法。
3.若是自定義對象做爲Map的鍵,那麼必須重寫hashCode和equals。
說明:String重寫了hashCode和equals方法,因此咱們能夠很是愉快地使用String對象做爲key來使用。

【強制】 ArrayList的subList結果不可強轉成ArrayList,不然會拋出ClassCastException異常,即java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。

說明:subList 返回的是 ArrayList 的內部類 SubList,並非 ArrayList而是ArrayList 的一個視圖,對於SubList子列表的全部操做最終會反映到原列表上。

【強制】在subList場景中,高度注意對原集合元素的增長或刪除,均會致使子列表的遍歷、增長、刪除產生ConcurrentModificationException 異常。

【強制】使用集合轉數組的方法,必須使用集合的toArray(T[] array),傳入的是類型徹底同樣的數組,大小就是list.size()。

說明:使用toArray帶參方法,入參分配的數組空間不夠大時,toArray方法內部將從新分配內存空間,並返回新數組地址;若是數組元素個數大於實際所需,下標爲[ list.size() ]的數組元素將被置爲null,其它數組元素保持原值,所以最好將方法入參數組大小定義與集合元素個數一致。
正例:
List list = new ArrayList(2);
list.add(「guan」);
list.add(「bao」);
String[] array = new String[list.size()];
array = list.toArray(array);
反例:直接使用toArray無參方法存在問題,此方法返回值只能Object[]類,若強轉其它類型數組將出現ClassCastException錯誤。

【強制】使用工具類Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方法,它的add/remove/clear方法會拋出UnsupportedOperationException異常。

說明:asList的返回對象是一個Arrays內部類,並無實現集合的修改方法。Arrays.asList體現的是適配器模式,只是轉換接口,後臺的數據還是數組。
String[] str = new String[] { 「you」, 「wu」 };
List list = Arrays.asList(str);
第一種狀況:list.add(「yangguanbao」); 運行時異常。
第二種狀況:str[0] = 「gujin」; 那麼list.get(0)也會隨之修改。

【強制】泛型通配符<? extends T>來接收返回的數據,此寫法的泛型集合不能使用add方法,而<? super T>不能使用get方法,做爲接口調用賦值時易出錯。

說明:擴展說一下PECS(Producer Extends Consumer Super)原則:第1、頻繁往外讀取內容的,適合用<? extends T>。第2、常常往裏插入的,適合用<? super T>。

【強制】不要在foreach循環裏進行元素的remove/add操做。remove元素請使用Iterator方式,若是併發操做,須要對Iterator對象加鎖。

正例:
List list = new ArrayList<>();
list.add(「1」);
list.add(「2」);
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (刪除元素的條件) {
iterator.remove();
}
}
反例:
for (String item : list) {
if (「1」.equals(item)) {
list.remove(item);
}
}
說明:以上代碼的執行結果確定會出乎你們的意料,那麼試一下把「1」換成「2」,會是一樣的結果嗎?答:正例執行成功;反例也執行成功,但把把「1」換成「2」會報ConcurrentModificationException.

【強制】 在JDK7版本及以上,Comparator實現類要知足以下三個條件,否則Arrays.sort,Collections.sort會報IllegalArgumentException異常。

說明:三個條件以下
1) x,y的比較結果和y,x的比較結果相反。
2) x>y,y>z,則x>z。
3) x=y,則x,z比較結果和y,z比較結果相同。
反例:下例中沒有處理相等的狀況,實際使用中可能會出現異常:
new Comparator() {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() > o2.getId() ? 1 : -1;
}
};

【推薦】集合泛型定義時,在JDK7及以上,使用diamond語法或全省略。

說明:菱形泛型,即diamond,直接使用<>來指代前邊已經指定的類型。
正例:
// <> diamond方式
HashMap<String, String> userCache = new HashMap<>(16);
// 全省略方式
ArrayList users = new ArrayList(10);

【推薦】集合初始化時,指定集合初始值大小。

說明:HashMap使用HashMap(int initialCapacity) 初始化。
正例:initialCapacity = (須要存儲的元素個數 / 負載因子) + 1。注意負載因子(即loader
factor)默認爲0.75,若是暫時沒法肯定初始值大小,請設置爲16(即默認值)。
反例:HashMap須要放置1024個元素,因爲沒有設置容量初始大小,隨着元素不斷增長,容量7次被迫擴大,resize須要重建hash表,嚴重影響性能。

【推薦】使用entrySet遍歷Map類集合KV,而不是keySet方式進行遍歷。

說明:keySet實際上是遍歷了2次,一次是轉爲Iterator對象,另外一次是從hashMap中取出key所對應的value。而entrySet只是遍歷了一次就把key和value都放到了entry中,效率更高。若是是JDK8,使用Map.foreach方法。
正例:values()返回的是V值集合,是一個list集合對象;keySet()返回的是K值集合,是一個Set集合對象;entrySet()返回的是K-V值組合集合。

【推薦】高度注意Map類集合K/V能不能存儲null值的狀況,以下表格:

集合類 Key Value Super 說明
Hashtable 不容許爲null 不容許爲null Dictionary 線程安全
ConcurrentHashMap 不容許爲null 不容許爲null AbstractMap 鎖分段技術(JDK8:CAS)
TreeMap 不容許爲null 容許爲null AbstractMap 線程不安全
HashMap 容許爲null 容許爲null AbstractMap 線程不安全

反例: 因爲HashMap的干擾,不少人認爲ConcurrentHashMap是能夠置入null值,而事實上,存儲null值時會拋出NPE異常。

【參考】合理利用好集合的有序性(sort)和穩定性(order),避免集合的無序性(unsort)和不穩定性(unorder)帶來的負面影響。

說明:有序性是指遍歷的結果是按某種比較規則依次排列的。穩定性指集合每次遍歷的元素次序是必定的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是order/sort。

【參考】利用Set元素惟一的特性,能夠快速對一個集合進行去重操做,避免使用List的contains方法進行遍歷、對比、去重操做。

(六) 併發處理

【強制】建立線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。
【強制】線程資源必須經過線程池提供,不容許在應用中自行顯式建立線程。
說明:使用線程池的好處是減小在建立和銷燬線程上所消耗的時間以及系統資源的開銷,解決
資源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或
者「過分切換」的問題。
【強制】線程池不容許使用Executors去建立,而是經過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險。
說明:Executors返回的線程池對象的弊端以下:
1)FixedThreadPool和SingleThreadPool:
容許的請求隊列長度爲Integer.MAX_VALUE,可能會堆積大量的請求,從而致使OOM。
2)CachedThreadPool和ScheduledThreadPool:
容許的建立線程數量爲Integer.MAX_VALUE,可能會建立大量的線程,從而致使OOM。
【強制】SimpleDateFormat 是線程不安全的類,通常不要定義爲static變量,若是定義爲static,必須加鎖,或者使用DateUtils工具類。
正例:注意線程安全,使用DateUtils。亦推薦以下處理:
private static final ThreadLocal df = new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat(「yyyy-MM-dd」);
}
};
說明:若是是JDK8的應用,可使用Instant代替Date,LocalDateTime代替Calendar,
DateTimeFormatter代替SimpleDateFormat,官方給出的解釋:simple beautiful strong
immutable thread-safe。
【強制】高併發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。
說明:儘量使加鎖的代碼塊工做量儘量的小,避免在鎖代碼塊中調用RPC方法。
【強制】併發修改同一記錄時,避免更新丟失,須要加鎖。要麼在應用層加鎖,要麼在緩存加鎖,要麼在數據庫層使用樂觀鎖,使用version做爲更新依據。
說明:若是每次訪問衝突機率小於20%,推薦使用樂觀鎖,不然使用悲觀鎖。樂觀鎖的重試次
數不得小於3次。
【強制】多線程並行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務便會自動終止運行,使用ScheduledExecutorService則沒有這個問題。
【推薦】使用CountDownLatch進行異步轉同步操做,每一個線程退出前必須調用countDown方法,線程執行代碼注意catch異常,確保countDown方法被執行到,避免主線程沒法執行至await方法,直到超時才返回結果。
說明:注意,子線程拋出異常堆棧,不能在主線程try-catch到。
【推薦】避免Random實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed 致使的性能降低。
說明:Random實例包括java.util.Random 的實例或者 Math.random()的方式。
正例:在JDK7以後,能夠直接使用API ThreadLocalRandom,而在 JDK7以前,須要編碼保證每一個線程持有一個實例。
【參考】volatile解決多線程內存不可見問題。
對於一寫多讀,是能夠解決變量同步問題,可是若是多寫,一樣沒法解決線程安全問題。若是是count++操做,使用以下類實現:
AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 若是是JDK8,推薦使用LongAdder對象,比AtomicLong性能更好(減小樂觀鎖的重試次數)。
【參考】 HashMap在容量不夠進行resize時因爲高併發可能出現死鏈,致使CPU飆升,在開發過程當中可使用其它數據結構或加鎖來規避此風險

(七) 控制語句

【強制】在一個switch塊內,每一個case要麼經過break/return等來終止,要麼註釋說明程序將繼續執行到哪個case爲止;在一個switch塊內,都必須包含一個default語句而且放在最後,即便空代碼。
【強制】在高併發場景中,避免使用」等於」判斷做爲中斷或退出的條件。
說明:若是併發控制沒有處理好,容易產生等值判斷被「擊穿」的狀況,使用大於或小於的區間判斷條件來代替。
反例:判斷剩餘獎品數量等於0時,終止發放獎品,但由於併發處理錯誤致使獎品數量瞬間變成了負數,這樣的話,活動沒法終止。
【推薦】循環體中的語句要考量性能,如下操做盡可能移至循環體外處理,如定義對象、變量、獲取數據庫鏈接,進行沒必要要的try-catch操做(這個try-catch是否能夠移至循環體外)。
【推薦】接口入參保護,這種場景常見的是用做批量操做的接口。
【參考】下列情形,須要進行參數校驗
1) 調用頻次低的方法。
2) 執行時間開銷很大的方法。此情形中,參數校驗時間幾乎能夠忽略不計,但若是由於參
數錯誤致使中間執行回退,或者錯誤,那得不償失。
3) 須要極高穩定性和可用性的方法。
4) 對外提供的開放接口,無論是RPC/API/HTTP接口。
5) 敏感權限入口。
【參考】下列情形,不須要進行參數校驗
1) 極有可能被循環調用的方法。但在方法說明裏必須註明外部參數檢查要求。
2) 底層調用頻度比較高的方法。畢竟是像純淨水過濾的最後一道,參數錯誤不太可能到底
層纔會暴露問題。通常DAO層與Service層都在同一個應用中,部署在同一臺服務器中,所
以DAO的參數校驗,能夠省略。
3) 被聲明成private只會被本身代碼所調用的方法,若是可以肯定調用方法的代碼傳入參
數已經作過檢查或者確定不會有問題,此時能夠不校驗參數。

(八) 註釋規約

【強制】類、類屬性、類方法的註釋必須使用Javadoc規範,使用/*內容/格式,不得使用// xxx方式。
說明:在IDE編輯窗口中,Javadoc方式會提示相關注釋,生成Javadoc能夠正確輸出相應註釋;在IDE中,工程調用方法時,不進入方法便可懸浮提示方法、參數、返回值的意義,提升閱讀效率(ps:idea默認window下使用ctrl+Q快捷鍵快速查看javadoc的註釋)。
【強制】全部的枚舉類型字段必需要有註釋,說明每一個數據項的用途。
【推薦】與其「半吊子」英文來註釋,不如用中文註釋把問題說清楚。專有名詞與關鍵字保持英文原文便可。
反例:「TCP鏈接超時」解釋成「傳輸控制協議鏈接超時」,理解反而費腦筋。
【參考】謹慎註釋掉代碼。在上方詳細說明,而不是簡單地註釋掉。若是無用,則刪除。
說明:代碼被註釋掉有兩種可能性:1)後續會恢復此段代碼邏輯。2)永久不用。前者若是沒有備註信息,難以知曉註釋動機。後者建議直接刪掉(代碼倉庫保存了歷史代碼)。
【參考】特殊註釋標記,請註明標記人與標記時間。注意及時處理這些標記,經過標記掃描,常常清理此類標記。線上故障有時候就是來源於這些標記處的代碼。
1) 待辦事宜(TODO):( 標記人,標記時間,[預計處理時間])
表示須要實現,但目前還未實現的功能。這其實是一個Javadoc的標籤,目前的Javadoc
尚未實現,但已經被普遍使用。只能應用於類,接口和方法(由於它是一個Javadoc標籤)。
2) 錯誤,不能工做(FIXME):(標記人,標記時間,[預計處理時間])
在註釋中用FIXME標記某代碼是錯誤的,並且不能工做,須要及時糾正的狀況。

(九) 其它

【強制】在使用正則表達式時,利用好其預編譯功能,能夠有效加快正則匹配速度。
說明:不要在方法體內定義:Pattern pattern = Pattern.compile(「規則」);

【強制】後臺輸送給頁面的變量必須加$!{var}——中間的感嘆號.
說明:若是var等於null或者不存在,那麼${var}會直接顯示在頁面上。

【強制】注意 Math.random() 這個方法返回是double類型,注意取值的範圍 0≤x<1(可以取到零值,注意除零異常),若是想獲取整數類型的隨機數,不要將x放大10的若干倍而後取整,直接使用Random對象的nextInt或者nextLong方法。
【強制】獲取當前毫秒數System.currentTimeMillis(); 而不是new Date().getTime();
說明:若是想獲取更加精確的納秒級時間值,使用System.nanoTime()的方式。在JDK8中,
針對統計時間等場景,推薦使用Instant類。
【推薦】及時清理再也不使用的代碼段或配置信息。
說明:對於垃圾代碼或過期配置,堅定清理乾淨,避免程序過分臃腫,代碼冗餘。
正例:對於暫時被註釋掉,後續可能恢復使用的代碼片段,在註釋代碼上方,統一規定使用三
個斜槓(///)來講明註釋掉代碼的理由。

2、異常日誌

(一) 異常處理

【強制】異常不要用來作流程控制,條件控制。
說明:異常設計的初衷是解決程序運行中的各類意外狀況,且異常的處理效率比條件判斷方式要低不少。
【強制】catch時請分清穩定代碼和非穩定代碼,穩定代碼指的是不管如何不會出錯的代碼。對於非穩定代碼的catch儘量進行區分異常類型,再作對應的異常處理。
說明:對大段代碼進行try-catch,使程序沒法根據不一樣的異常作出正確的應激反應,也不利於定位問題,這是一種不負責任的表現。
正例:用戶註冊的場景中,若是用戶輸入非法字符,或用戶名稱已存在,或用戶輸入密碼過於簡單,在程序上做出分門別類的判斷,並提示給用戶。
【強制】有try塊放到了事務代碼中,catch異常後,若是須要回滾事務,必定要注意手動回滾事務。
【強制】finally塊必須對資源對象、流對象進行關閉,有異常也要作try-catch。
說明:若是JDK7及以上,可使用try-with-resources方式。
【強制】不要在finally塊中使用return。
說明:finally塊中的return返回後方法結束執行,不會再執行try塊中的return語句。
【推薦】方法的返回值能夠爲null,不強制返回空集合,或者空對象等,必須添加註釋充分說明什麼狀況下會返回null值。
說明:本手冊明確防止NPE是調用者的責任。即便被調用方法返回空集合或者空對象,對調用阿里巴巴Java開發手冊 者來講,也並不是高枕無憂,必須考慮到遠程調用失敗、序列化失敗、運行時異常等場景返回
null的狀況。
【推薦】防止NPE,是程序員的基本修養,注意NPE產生的場景
1)返回類型爲基本數據類型,return包裝數據類型的對象時,自動拆箱有可能產生NPE。
反例:public int f() { return Integer對象}, 若是爲null,自動解箱拋NPE。
2) 數據庫的查詢結果可能爲null。
3) 集合裏的元素即便isNotEmpty,取出的數據元素也可能爲null。
4) 遠程調用返回對象時,一概要求進行空指針判斷,防止NPE。
5) 對於Session中獲取的數據,建議NPE檢查,避免空指針。
6) 級聯調用obj.getA().getB().getC();一連串調用,易產生NPE。
正例:使用JDK8的Optional類來防止NPE問題。
【參考】避免出現重複的代碼(Don’t Repeat Yourself),即DRY原則。
說明:隨意複製和粘貼代碼,必然會致使代碼的重複,在之後須要修改時,須要修改全部的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是組件化。
正例:一個類中有多個public方法,都須要進行數行相同的參數校驗操做,這個時候請抽取:
private boolean checkParam(DTO dto) {…}

(二) 日誌規約

【強制】應用中不可直接使用日誌系統(Log4j、Logback)中的API,而應依賴使用日誌框架SLF4J中的API,使用門面模式的日誌框架,有利於維護和各個類的日誌處理方式統一
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);
ps:本人建議使用lombok的@slf4j註解
【強制】日誌文件至少保存15天,由於有些異常具有以「周」爲頻次發生的特色。
【強制】對trace/debug/info級別的日誌輸出,必須使用條件輸出形式或者使用佔位符的方式。
說明:logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
若是日誌級別是warn,上述日誌不會打印,可是會執行字符串拼接操做,若是symbol是對象,
會執行toString()方法,浪費了系統資源,執行了上述操做,最終日誌卻沒有打印。
正例:(條件)建設採用以下方式
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
}
正例:(佔位符)
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
【推薦】謹慎地記錄日誌。生產環境禁止輸出debug日誌;有選擇地輸出info日誌;若是使用warn來記錄剛上線時的業務行爲信息,必定要注意日誌輸出量的問題,避免把服務器磁盤撐爆,並記得及時刪除這些觀察日誌。
說明:大量地輸出無效日誌,不利於系統性能提高,也不利於快速定位錯誤點。記錄日誌時請
思考:這些日誌真的有人看嗎?看到這條日誌你能作什麼?能不能給問題排查帶來好處?
【推薦】可使用warn日誌級別來記錄用戶輸入參數錯誤的狀況,避免用戶投訴時,無所適從。如非必要,請不要在此場景打出error級別,避免頻繁報警。
說明:注意日誌輸出的級別,error級別只記錄系統邏輯出錯、異常或者重要的錯誤信息。
【推薦】儘可能用英文來描述日誌錯誤信息,若是日誌中的錯誤信息用英文描述不清楚的話使用中文描述便可,不然容易產生歧義。國際化團隊或海外部署的服務器因爲字符集問題,【強制】使用全英文來註釋和描述日誌錯誤信息。

未完待續…

參考:


  1. https://github.com/alibaba/p3c/blob/master/阿里巴巴Java開發手冊(詳盡版).pdf ↩︎

  2. http://www.javashuo.com/article/p-adqpmigx-dq.html ↩︎

相關文章
相關標籤/搜索