摘自《阿里巴巴Java開發手冊》,只整理認爲須要注意的部分html
推薦使用String[] args
,明確聲明瞭參數類型爲String[]
,而String args[]
只聲明爲String
java
抽象類(不是接口)命名用Abstract/ Base開頭,異常類名以Exception結尾
AbstractUserService BaseUserService BusinessException
程序員
Reason:部分框架解析錯誤
反例:boolean isSuccess
定義爲基本數據類型boolean isSuccess;
的屬性,它的方法也是isSuccess()
,RPC框架在反向解析的時候,「覺得」對應的屬性名稱是success,致使屬性獲取不到,進而拋出異常。正則表達式
類名帶上Enum後綴
成員名稱大小,下劃線分隔
正例:枚舉名字:DealStatusEnum;成員名稱:SUCCESS / UNKOWN_REASON數據庫
getXxx
listXxx
countXxx
saveXxx
/ insertXxx
removeXxx
/ deleteXxx
updateXxx
反例:String key="Id#taobao_"+tradeId;
編程
必須大寫
正例:Long a = 2L
反例:Long a = 2l
windows
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);
複製代碼
換行符使用Unix格式,不要使用windows格式
系統 | 換行符 |
---|---|
Windows | \n\r |
Unix | \n |
Mac | \r |
直接類名訪問
避免經過對象引用訪問,無謂增長編譯器解析成本
覆寫方法加@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);
}
複製代碼
全部的POJO類屬性必須使用包裝數據類型
RPC(遠程過程調用協議)方法的返回值和參數必須使用包裝數據類型
全部的局部變量推薦使用基本數據類型
序列化類新增屬性時,請不要修改serialVersionUID字段,避免反序列失敗;
若是徹底不兼容升級,避免反序列化混亂,那麼請修改serialVersionUID值
注意serialVersionUID不一致會拋出序列化運行時異常
構造方法裏面禁止加入任何業務邏輯,若是有初始化邏輯,請放在init方法
都加上toString()
,若有繼承,前面加上super.toString()
拋出異常時能夠調用toString方法打印屬性值,方便排查問題
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場景中,高度注意對原集合元素個數的修改,會致使子列表的遍歷、增長、刪除均產生ConcurrentModificationException 異常
Arraylist中有成員變量
protected transient int modCount = 0;
當在原集合中修改元素個數後(結構性修改structurally modified,增刪元素,更新元素就不會),modCount++
,記錄下修改的次數
而在subList,因爲記錄下的modCount仍是原來的modCount,與原集合的沒有同步,就會拋出異常 另,若在sublist中有結構性修改,那就能夠反映到原集合中,不會報錯,反之就不行
轉數組使用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();
}
}
複製代碼
數組類以插入元素數量指定大小,map set類型則需比實際數量稍大一點(maybe 10%)
what-is-the-optimal-capacity-and-load-factor-for-a-fixed-size-hashmap
集合類 | Key | Value | Super | 說明 |
---|---|---|---|---|
Hashtable | not null | not null | Dictionary | 線程安全 |
ConcurrentHashMap | not null | not null | AbstractMap | 線程局部安全 |
TreeMap | not null | null | AbstractMap | 線程不安全 |
HashMap | null | null | AbstractMap | 線程不安全 |
獲取單例對象要線程安全。在單例對象裏面作操做也要保證線程安全。 http://blog.csdn.net/cselmu9/article/details/51366946
線程資源必須經過線程池提供,不容許在應用中自行顯式建立線程;
使用線程池的好處是減小在建立和銷燬線程上所花的時間以及系統資源的開銷,解決資源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或者「過分切換」的問題
http://www.cnblogs.com/dolphin0520/p/3932921.html
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各個方法的弊端:
1) newFixedThreadPool和newSingleThreadExecutor:
主要問題是堆積的請求處理隊列可能會耗費很是大的內存,甚至OOM
2) newCachedThreadPool和newScheduledThreadPool:
主要問題是線程數最大數是Integer.MAX_VALUE,可能會建立數量很是多的線程,甚至OOM
方便出錯時回溯
使用CountDownLatch進行異步轉同步操做,每一個線程退出前必須調用countDown方法,線程執行代碼注意catch異常,確保countDown方法能夠執行,避免主線程沒法執行至countDown方法,直到超時才返回結果 注意,子線程拋出異常堆棧,不能在主線程try-catch到。
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 resize時出現死循環,
https://my.oschina.net/hosee/blog/673521
【參考】ThreadLocal沒法解決共享對象的更新問題,ThreadLocal對象建議使用static修飾。這個變量是針對一個線程內全部操做共有的,因此設置爲靜態變量,全部此類實例共享此靜態變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,全部此類的對象(只要是這個線程內定義的)均可以操控這個變量。
在一個switch塊內,都必須包含一個default語句而且放在最後,即便它什麼代碼也沒有
Condition 條件儘可能簡單,複雜則抽出 儘可能少用else,if-else的方式能夠改寫成
if(condition){
…
return obj;
}
// 接着寫else的業務邏輯代碼;
複製代碼
若是使用要if-else if-else方式表達邏輯,【強制】請勿超過3層,超過請使用狀態設計模式
每一個枚舉字段都需註釋,說明每一個數據項用途
在使用正則表達式時,利用好其預編譯功能
不要在方法體內定義:Pattern pattern = Pattern.compile(規則);
避免用Apache Beanutils進行屬性的copy,Apache BeanUtils性能較差,可使用其餘方案好比Spring BeanUtils, Cglib BeanCopier
使用System.currentTimeMillis();
而不是new Date().getTime();
在JDK8中,針對統計時間等場景,推薦使用Instant類
不要捕獲Java類庫中定義的繼承自RuntimeException的運行時異常類,如:IndexOutOfBoundsException / NullPointerException,這類異常由程序員預檢查來規避,保證程序健壯性。
正例:if(obj != null) {...}
反例:try { obj.method() } catch(NullPointerException e){…}
只try catch最小非穩定代碼,並區分異常類型,分別處理,或者傳給上層業務使用者,業務使用者必須處理異常,將其轉化爲用戶能夠理解的內容
不能在finally塊中使用return,finally塊中的return返回後方法結束執行,不會再執行try塊中的return語句
當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回以前被執行。在如下4種特殊狀況下,finally塊不會被執行:
1)在finally語句塊中發生了異常。
2)在前面的代碼中用了System.exit()
退出程序。
3)程序所在的線程死亡。
4)關閉CPU。
方法的返回值能夠爲null,不強制返回空集合,或者空對象等,必須添加註釋充分說明什麼狀況下會返回null值。
調用方須要進行null判斷防止NPE問題
集合裏的元素即便isNotEmpty,取出的數據元素也可能爲null
公司外的http/api開放接口必須使用「錯誤碼」;
應用內部推薦異常拋出
日誌文件推薦至少保存15天,由於有些異常具有以「周」爲頻次發生的特色
異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。若是不處理,那麼往上拋
正例:logger.error(各種參數或者對象toString + "_" + e.getMessage(), e);
使用is_xxx
的方式命名,數據類型是unsigned tinyint
( 1表示是,0表示否)
任何字段若是爲非負數,必須是unsigned
小數類型爲decimal,禁止使用float和double,會損失精度
若是存儲的數據範圍超過decimal的範圍,建議將數據拆成整數和小數分開存儲
表中字段儲存長度過大,爲text類型,獨立出來,用主鍵對應,避免影響其餘字段索引效率
業務上具備惟一特性的字段,即便是組合字段,也必須建成惟一索引
在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沒法排序。
使用count(*)
,不使用count(列名)
方式,count(*)
會統計值爲NULL的行,而count(列名)不會統計此列爲NULL值的行
count(distinct col)
計算該列除NULL以外的不重複數量
注意 count(distinct col1, col2)
若是其中一列全爲NULL,那麼即便另外一列有不一樣的值,也返回爲0。
禁止使用存儲過程,存儲過程難以調試和擴展,更沒有移植性
TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少,但TRUNCATE無事務且不觸發trigger,有可能形成事故,故不建議在開發代碼中使用此語句。
說明:TRUNCATE TABLE 在功能上與不帶 WHERE 子句的 DELETE 語句相同。
使用:#{}
,#param#
不要使用${}
此種方式容易出現SQL注入
<isEqual>
中的compareValue是與屬性值對比的常量,通常是數字,表示相等時帶上此條件;
<isNotEmpty>
表示不爲空且不爲null時執行;
<isNotNull>
表示不爲null值時執行。