Java規範

摘自《阿里巴巴Java開發手冊》,只整理認爲須要注意的部分html

1、 編程規範

1.1 命名規範

參數類型String[] args

推薦使用String[] args ,明確聲明瞭參數類型爲String[],而String args[]只聲明爲Stringjava

特殊類名先後綴

抽象類(不是接口)命名用Abstract/ Base開頭,異常類名以Exception結尾
AbstractUserService BaseUserService BusinessException程序員

布爾變量不加is前綴(POJO類中)

Reason:部分框架解析錯誤
反例:boolean isSuccess 定義爲基本數據類型boolean isSuccess;的屬性,它的方法也是isSuccess(),RPC框架在反向解析的時候,「覺得」對應的屬性名稱是success,致使屬性獲取不到,進而拋出異常。正則表達式

枚舉名稱定義

類名帶上Enum後綴
成員名稱大小,下劃線分隔
正例:枚舉名字:DealStatusEnum;成員名稱:SUCCESS / UNKOWN_REASON數據庫

各層命名規約

  • 方法命名
    獲取單個對象 getXxx
    獲取多個對象listXxx
    獲取統計值 countXxx
    插入saveXxx / insertXxx
    刪除 removeXxx / deleteXxx
    修改 updateXxx
  • 領域模型命名
    POJO爲統稱,禁止命名成xxxPOJO
    數據表xxx對象:xxxDO
    數據傳輸對象:xxxDTO
    頁面xxx展現對象xxxVO

1.2 常量定義

禁止魔法值

反例:String key="Id#taobao_"+tradeId;編程

Long型賦值

必須大寫
正例:Long a = 2L
反例:Long a = 2lwindows

1.3 格式規約

保留字括號空格

if/for/while/switch/do等保留字與左右括號之間都必須加空格設計模式

換行

相對上一行縮進一個tab
運算符,點(.)符號與下文一塊兒換行
括號前,逗號不換行
正例:api

sb.append("zi").append("xin")… 
    .append("huang")……
    + aaa + …;
複製代碼

反例:數組

sb.append("zi").	//.號
append("xin"). append
 ("huang");		//括號前不換行
//參數不少的方法調用也超過120個字符,逗號後纔是換行處 
method(args1, args2, args3, ... 
, argsX);
複製代碼

IDE文件換行符

換行符使用Unix格式,不要使用windows格式

系統 換行符
Windows \n\r
Unix \n
Mac \r

eclipse設置換行符

1.4 OOP規約

訪問靜態變量/靜態方法

直接類名訪問
避免經過對象引用訪問,無謂增長編譯器解析成本

@Override

覆寫方法加@Override註解,編譯檢查,能夠判斷是否覆蓋成功

接口簽名修改

原則上不容許修改,接口過期使用@Deprecated註解,並註明新接口是什麼

值比較

使用工具類java.util.Objects.equals(a, b);
包裝類務必使用equals方法
對於Integer var=?在-128至127之間的賦值,Integer對象是在IntegerCache.cache產生,會複用已有對象,這個區間內的Integer值能夠直接使用==進行判斷,可是這個區間以外的全部數據,都會在堆上產生,並不會複用已有對象,這是一個大坑,推薦使用equals方法進行判斷。
Long short類型也是如此

public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
    	return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
複製代碼

基本數據類型 vs 包裝數據類型

全部的POJO類屬性必須使用包裝數據類型
RPC(遠程過程調用協議)方法的返回值和參數必須使用包裝數據類型
全部的局部變量推薦使用基本數據類型

序列化

序列化類新增屬性時,請不要修改serialVersionUID字段,避免反序列失敗;
若是徹底不兼容升級,避免反序列化混亂,那麼請修改serialVersionUID值
注意serialVersionUID不一致會拋出序列化運行時異常

業務邏輯初始化

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

POJO toString()方法

都加上toString(),若有繼承,前面加上super.toString()
拋出異常時能夠調用toString方法打印屬性值,方便排查問題

1.5 集合處理

Map / Set key值重寫hashCode和equals

ArrayList vs subList

ArrayList的subList結果不可強轉成ArrayList
subList 返回的是 ArrayList 的內部類 SubList,並非 ArrayList ,而是 ArrayList 的一個視圖,對於SubList子列表的全部操做最終會反映到原列表上

public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}
複製代碼
private class SubList extends AbstractList<E> implements RandomAccess 複製代碼

Arrays.asList()返回的也是Arrays內部類,實際上後臺數據仍爲數組

sublist

在subList場景中,高度注意對原集合元素個數的修改,會致使子列表的遍歷、增長、刪除均產生ConcurrentModificationException 異常
Arraylist中有成員變量
protected transient int modCount = 0;
當在原集合中修改元素個數後(結構性修改structurally modified,增刪元素,更新元素就不會),modCount++,記錄下修改的次數
而在subList,因爲記錄下的modCount仍是原來的modCount,與原集合的沒有同步,就會拋出異常 另,若在sublist中有結構性修改,那就能夠反映到原集合中,不會報錯,反之就不行

集合轉數組 toArray(T[] array)

轉數組使用toArray(T[] array)
若使用toArray(),返回的Object[] 類型,在作轉型時可能出現ClassCastException

List<String> list = new ArrayList<String>(2); 
list.add("guan"); 
list.add("bao"); 
//指定size,不然默認返回的多餘數組值爲null
String[] array = new String[list.size()]; 
array = list.toArray(array);
複製代碼

集合通配符

泛型通配符<? extends T>來接收返回的數據,此寫法的泛型集合不能使用add方法。 說明:蘋果裝箱後返回一個<? extends Fruits>對象,此對象就不能往裏加任何水果,包括蘋果。 增刪集合元素
不要在foreach循環裏進行元素的remove/add操做,能夠用下標值/ Iterator增刪
反例:

List<String> a = new ArrayList<String>(); 
    a.add("1"); 
    a.add("2"); 
    for (String temp : a) { 
    if("1".equals(temp)){
     a.remove(temp); 
    } 
} 
複製代碼

正例: 單線程環境

Iterator<String> it = a.iterator(); 
    while(it.hasNext()){ 
    	String temp = it.next(); 
    	if(刪除元素的條件){ 
    	it.remove();	 //not a.remove();
    } 
}
複製代碼

多線程:CopyOnWriteArrayList,CopyOnWriteArraySet,每次add,remove等全部的操做都是從新建立一個新的數組,再把引用指向新的數組。這樣即可實現線程安全,也不需modCount域,因此對其進行remove和add不會拋出併發異常

Iterator 中內部類實現

public void remove() {
    if (lastRet < 0)
    	throw new IllegalStateException();
    checkForComodification();
    
    try {
    	ArrayList.this.remove(lastRet);
    	cursor = lastRet;
    	lastRet = -1;
    	expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
    	throw new ConcurrentModificationException();
    }
}
複製代碼
  • COW(Copy-On-Write)
    CopyOnWrite容器即寫時複製的容器。當咱們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,複製出一個新的容器,而後新的容器裏添加元素,添加完元素以後,再將原容器的引用指向新的容器。
    這樣作的好處是咱們能夠對CopyOnWrite容器進行併發的讀,而不須要加鎖,由於當前容器不會添加任何元素。因此CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不一樣的容器。
  • fail-fast(快速失敗機制)
    是Java集合的一種是爲了防止多線程修改集合形成併發問題的機制。當多個線程對集合進行結構上的改變的操做時,modCount與expectedModCount不一樣會產生異常。

集合初始化大小

數組類以插入元素數量指定大小,map set類型則需比實際數量稍大一點(maybe 10%)
what-is-the-optimal-capacity-and-load-factor-for-a-fixed-size-hashmap

K/V存儲null

集合類 Key Value Super 說明
Hashtable not null not null Dictionary 線程安全
ConcurrentHashMap not null not null AbstractMap 線程局部安全
TreeMap not null null AbstractMap 線程不安全
HashMap null null AbstractMap 線程不安全

1.6 併發處理

單例線程安全

獲取單例對象要線程安全。在單例對象裏面作操做也要保證線程安全。 http://blog.csdn.net/cselmu9/article/details/51366946

線程由線程池提供

線程資源必須經過線程池提供,不容許在應用中自行顯式建立線程;
使用線程池的好處是減小在建立和銷燬線程上所花的時間以及系統資源的開銷,解決資源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或者「過分切換」的問題
http://www.cnblogs.com/dolphin0520/p/3932921.html

日期類SimpleDateFormat & DateUtils

SimpleDateFormat 是線程不安全的類,通常不要定義爲static變量,若是定義爲static,必須加鎖,或者使用自定義的DateUtils工具類。
正例:注意線程安全,使用DateUtils。亦推薦以下處理:

private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){ 
    @Override 
    protected DateFormat initialValue() { 
        return new SimpleDateFormat("yyyy-MM-dd"); 
} 
複製代碼

https://my.oschina.net/leejun2005/blog/152253

鎖的使用

高併發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖

併發修改

併發修改同一記錄時,避免更新丟失,要麼在應用層加鎖,要麼在緩存加鎖,要麼在數據庫層使用樂觀鎖,使用version做爲更新依據。
若是每次訪問衝突機率小於20%,推薦使用樂觀鎖,不然使用悲觀鎖。樂觀鎖的重試次數不得小於3次
樂觀鎖:每次讀取時認爲數據不會修改,讀寫不加鎖,更新時查看版本號是否一致
悲觀鎖:每次讀取數據時認爲數據可能會被修改,故讀寫加鎖

多線程定時任務

多線程並行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務便會自動終止運行,使用ScheduledExecutorService則沒有這個問題。

不使用Executors,用ThreadPoolExecutor建立線程池

讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險。
Executors各個方法的弊端:
1) newFixedThreadPool和newSingleThreadExecutor:
主要問題是堆積的請求處理隊列可能會耗費很是大的內存,甚至OOM
2) newCachedThreadPool和newScheduledThreadPool:
主要問題是線程數最大數是Integer.MAX_VALUE,可能會建立數量很是多的線程,甚至OOM

線程/線程池建立時指定有意義的名稱

方便出錯時回溯

CountDownLatch 必須執行 countDown方法退出

使用CountDownLatch進行異步轉同步操做,每一個線程退出前必須調用countDown方法,線程執行代碼注意catch異常,確保countDown方法能夠執行,避免主線程沒法執行至countDown方法,直到超時才返回結果 注意,子線程拋出異常堆棧,不能在主線程try-catch到。

雙重檢查鎖 volatile

The "Double-Checked Locking is Broken" Declaration
freish.iteye.com/blog/100830…

class Foo { 
    private volatile Helper helper = null; 
    public Helper getHelper() {
    	if (helper == null) {
            synchronized(this) { 
            if (helper == null) 
                helper = new Helper(); 
            } 
    	}
    return helper; 
} 
複製代碼

【參考】注意HashMap的擴容死鏈,致使CPU飆升的問題。

在多線編程環境下,因爲沒有同步,就可能致使hashmap resize時出現死循環,
https://my.oschina.net/hosee/blog/673521

擴容死鏈

ThreadLocal沒法解決共享對象的更新

【參考】ThreadLocal沒法解決共享對象的更新問題,ThreadLocal對象建議使用static修飾。這個變量是針對一個線程內全部操做共有的,因此設置爲靜態變量,全部此類實例共享此靜態變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,全部此類的對象(只要是這個線程內定義的)均可以操控這個變量。

1.7 控制語句

Switch

在一個switch塊內,都必須包含一個default語句而且放在最後,即便它什麼代碼也沒有

If else 判斷

Condition 條件儘可能簡單,複雜則抽出 儘可能少用else,if-else的方式能夠改寫成

if(condition){
	… 
	return obj; 
} 
// 接着寫else的業務邏輯代碼;
複製代碼

若是使用要if-else if-else方式表達邏輯,【強制】請勿超過3層,超過請使用狀態設計模式

參數校驗

  • 須要校驗:
    1) 調用頻次低的方法。
    2) 執行時間開銷很大的方法,參數校驗時間幾乎能夠忽略不計,但若是由於參數錯誤致使中間執行回退,或者錯誤,那得不償失。
    3) 須要極高穩定性和可用性的方法。
    4) 對外提供的開放接口,不論是RPC/API/HTTP接口。
  • 不須要校驗
    1) 極有可能被循環調用的方法,不建議對參數進行校驗。但在方法說明裏必須註明外部參數檢查。
    2) 底層的方法調用頻度都比較高,通常不校驗。畢竟是像純淨水過濾的最後一道,參數錯誤不太可能到底層纔會暴露問題。通常DAO層與Service層都在同一個應用中,部署在同一臺服務器中,因此DAO的參數校驗,能夠省略
    3) 被聲明成private只會被本身代碼所調用的方法,若是可以肯定調用方法的代碼傳入參數已經作過檢查或者確定不會有問題,此時能夠不校驗參數。

1.8 註釋規約

每一個枚舉字段都需註釋,說明每一個數據項用途

1.9 其餘

預編譯正則表達式

在使用正則表達式時,利用好其預編譯功能
不要在方法體內定義:Pattern pattern = Pattern.compile(規則);

Bean 屬性copy

避免用Apache Beanutils進行屬性的copy,Apache BeanUtils性能較差,可使用其餘方案好比Spring BeanUtils, Cglib BeanCopier

獲取時間

使用System.currentTimeMillis(); 而不是new Date().getTime();
在JDK8中,針對統計時間等場景,推薦使用Instant類

2、異常日誌

2.1 異常處理

異常類層次關係

不捕獲RuntimeException子類

不要捕獲Java類庫中定義的繼承自RuntimeException的運行時異常類,如:IndexOutOfBoundsException / NullPointerException,這類異常由程序員預檢查來規避,保證程序健壯性。
正例:if(obj != null) {...}
反例:try { obj.method() } catch(NullPointerException e){…}

try catch

只try catch最小非穩定代碼,並區分異常類型,分別處理,或者傳給上層業務使用者,業務使用者必須處理異常,將其轉化爲用戶能夠理解的內容

finally return

不能在finally塊中使用return,finally塊中的return返回後方法結束執行,不會再執行try塊中的return語句
當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回以前被執行。在如下4種特殊狀況下,finally塊不會被執行:
1)在finally語句塊中發生了異常。
2)在前面的代碼中用了System.exit()退出程序。
3)程序所在的線程死亡。
4)關閉CPU。

Null 值返回

方法的返回值能夠爲null,不強制返回空集合,或者空對象等,必須添加註釋充分說明什麼狀況下會返回null值。
調用方須要進行null判斷防止NPE問題
集合裏的元素即便isNotEmpty,取出的數據元素也可能爲null

拋異常 vs 返回錯誤碼

公司外的http/api開放接口必須使用「錯誤碼」;
應用內部推薦異常拋出

2.2 日誌規約

日誌保存日期

日誌文件推薦至少保存15天,由於有些異常具有以「周」爲頻次發生的特色

異常信息

異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。若是不處理,那麼往上拋
正例:logger.error(各種參數或者對象toString + "_" + e.getMessage(), e);

3、MYSQL規約

3.1 建表規約

是否字段 0/1

使用is_xxx的方式命名,數據類型是unsigned tinyint( 1表示是,0表示否)
任何字段若是爲非負數,必須是unsigned

小數類型decimal

小數類型爲decimal,禁止使用float和double,會損失精度
若是存儲的數據範圍超過decimal的範圍,建議將數據拆成整數和小數分開存儲

Text:字段長短超過5000

表中字段儲存長度過大,爲text類型,獨立出來,用主鍵對應,避免影響其餘字段索引效率

3.2 索引規約

惟一索引

業務上具備惟一特性的字段,即便是組合字段,也必須建成惟一索引

varchar字段索引長度

在varchar字段上創建索引時,必須指定索引長度,不必對全字段創建索引,根據實際文本區分度決定索引長度
索引的長度與區分度是一對矛盾體,通常對字符串類型數據,長度爲20的索引,區分度會高達90%以上,可使用count(distinct left(列名, 索引長度))/count(*)的區分度來肯定。

索引排序

若是有order by的場景,請注意利用索引的有序性。order by 最後的字段是組合索引的一部分,而且放在索引組合順序的最後,避免出現file_sort的狀況,影響查詢性能
正例:where a=? and b=? order by c;索引:a_b_c
反例:索引中有範圍查找,那麼索引有序性沒法利用,如:WHERE a>10 ORDER BY b; 索引a_b沒法排序。

3.3 SQL規約

Count

使用count(*),不使用count(列名)方式,count(*)會統計值爲NULL的行,而count(列名)不會統計此列爲NULL值的行
count(distinct col)計算該列除NULL以外的不重複數量
注意 count(distinct col1, col2) 若是其中一列全爲NULL,那麼即便另外一列有不一樣的值,也返回爲0。

不使用存儲過程

禁止使用存儲過程,存儲過程難以調試和擴展,更沒有移植性

TRUNCATE vs DELETE

TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少,但TRUNCATE無事務且不觸發trigger,有可能形成事故,故不建議在開發代碼中使用此語句。
說明:TRUNCATE TABLE 在功能上與不帶 WHERE 子句的 DELETE 語句相同。

Xml配置參數

使用:#{}#param# 不要使用${} 此種方式容易出現SQL注入

動態值判斷

<isEqual>中的compareValue是與屬性值對比的常量,通常是數字,表示相等時帶上此條件;
<isNotEmpty>表示不爲空且不爲null時執行;
<isNotNull>表示不爲null值時執行。

相關文章
相關標籤/搜索