Java語言習慣用語
1。循環
在重要的迴圈裡,消除迴圈終止判斷時的方法呼叫。
例如:將
for(int i=0; i<collection.size();i++){ ... }
替換為…
for(int i=0; n=collection.size();i<n;i++){...}
通常,把與循環index不相關的移到循環的外面
for(int i=0; terminal=x.length;i<terminal;i++){x[i] = x[i]/scaleA *scaleB;}
應該該成:
Double scale = scaleB*scaleA;for(int i=0; terminal=x.length;i<terminal;i++){x[i] = x[i]/scale ;}
2。字串
消除字串連接
建立長字串時,總是使用StringBuffter代替String
預先分配StringBuffer空間StringBuffer sb = new StringBuffer(5000);
3。基本資料類型
在重要的迴圈裡使用基本資料型態(int型資料通常比long/double型資料更快)
基本資料型別(Boolean,Integer,etc)的包裝類別主要用在當傳遞的方法參數必須是一個物件的參考時(而不是一個基本資料型別)
對所有的常數代數表達式使用static final修飾符使常數更容易引用(編譯器預先計算常數表達式)
4。例外
異常只用於單一真正的錯誤條件如拋出異常時拋出一個異常和執行一個catch代碼塊花費是很高的(主要由於當創建一個異常時要獲得線程棧的一個快照)
只當條件真的異常時才拋出一個異常拋出異常首先要創建一個新的物件。
Throwable介面的建構子呼叫名為fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法檢查堆疊,收集呼叫追蹤資訊。
只要有異常被拋出,VM就必須調整呼叫堆疊,因為在處理過程中建立了一個新的物件。
異常只能用於錯誤處理,不應該用來控製程式流程。
使編譯器和運行時最優化,將幾個方法呼叫放在一個try/catch區塊中,而不是為每個方法呼叫實作幾個try/catch區塊
try{ Some.method1(); //Difficut for java1.4 }catch(method1Exception e){ handle exception
1 // to optimize this code } try{ Some.method2(); //Difficut for java1.4 }catch(method2Exception e){ handle exception
2 // to optimize this code } try{ Some.method3(); //Difficut for java1.4 }catch(method3Exception e){ handle exception
3 // to optimize this code
}
應該寫為:
try{ Some.method1(); Some.method2(); Some.method3(); //Difficut for java1.4 }catch(method1Exception e){ handle exception 1 }catch(method2Exception e){ handle exception 2 }catch( method3Exception e){ handle exception 3 }
5。基準
請注意,所有這些技巧會因不同的平台和虛擬機器而不同
一例如:在有些servlet容器內,透過一個OutputStream作為位元組輸出會更快
二在其它的容器內,透過一個PrintWriter輸出字元會更快
這些技巧描述的是最可移植的建議
你可能需要運行一些基準來判斷在你的平台上怎麼樣是最快的
6。不用new關鍵字創建類別的實例
用new關鍵字建立類別的實例時,建構函式鏈中的所有建構子都會自動呼叫。
但如果一個物件實作了Cloneable接口,我們可以呼叫它的clone()方法。 clone()方法不會呼叫任何類別建構子。
在使用設計模式(Design Pattern)的場合,如果用Factory模式建立對象,則改用clone()方法建立新的物件實例非常簡單。
例如,下面是Factory模式的一個典型實作:
public static Credit getNewCredit() { return new Credit(); }
優化後:
private static Credit BaseCredit = new Credit(); public static Credit getNewCredit() { return (Credit) BaseCredit.clone();}
上面的想法對於數組處理同樣很有用。
7。使用非阻塞I/O
Java版本較低的JDK不支援非阻塞I/O API。為避免I/O阻塞,有些應用採用了創建大量執行緒的辦法(在較好的情況下,會使用一個緩衝池)。這種技術可以在許多必須支援並發I/O串流的應用程式中見到,如Web伺服器、報價和拍賣應用程式等。然而,創建Java線程需要相當可觀的開銷。
JDK 1.4引入了非阻塞的I/O函式庫(java.nio)。如果應用程式要求使用版本較早的JDK,這裡有一個支援非阻塞I/O的軟體包。
8。不要重複初始化變數
預設情況下,當呼叫類別的建構子時, Java會把變數初始化成確定的值:所有的物件都設定成null,整數變數(byte、short、int、long)設定成0,float和double變數設定成0.0,邏輯值設定成false。
當一個類別從另一個類別派生時,這一點尤其應該注意,因為用new關鍵字創建一個物件時,建構函數鏈中的所有建構函數都會被自動呼叫。
9。盡量指定類別的final修飾符
帶有final修飾符的類別是不可派生的。在Java核心API中,有許多應用final的例子,例如java.lang.String。為String類別指定final防止了人們覆寫length()方法。
另外,如果指定一個類別為final,則該類別所有的方法都是final。 Java編譯器會尋找機會內聯(inline)所有的final方法(這和具體的編譯器實作有關)。此舉能夠使性能平均提高50%。
10。盡量使用局部變數
呼叫方法時傳遞的參數以及在呼叫中建立的臨時變數都保存在堆疊(Stack)中,速度較快。其他變量,如靜態變數、實例變數等,都在堆(Heap)中創建,速度較慢。另外,依賴特定的編譯器/JVM,局部變數還可能進一步最佳化。請參閱《盡可能使用堆疊變數》。
11。乘法和除法
考慮下面的程式碼:
for (val = 0; val < 100000; val +=5) { alterX = val * 8; myResult = val * 2; }
優化後:
for (val = 0; val < 100000; val += 5) { alterX = val << 3; myResult = val << 1; }
修改後的程式碼不再做乘以8的操作,而是改用等價的左移3位元操作,每左移1位元相當於乘以2。相應地,右移1位操作相當於除以2。值得一提的是,雖然移位操作速度快,但可能會使程式碼比較難於理解,所以最好加上一些註解。
private static Credit BaseCredit = new Credit();public static Credit getNewCredit() {return (Credit) BaseCredit.clone();}