很多程序員在一開始並不注重性能的設計,只有當系統交付運行時,才 發現問題並且開始解決這一問題,但往往這只能拯救一點點。性能的治理應該一開始 就被整合到設計和開發當中去。
最普遍的問題就是臨時對象大量經常的創建,這為性能埋下隱患。
性能的問題來自很多原因,最輕易解決的可能是:你選擇了不好的算法來進行計算,如 用冒泡法來排序巨量數據,或者你每次使用數據時都要反復計算一次,這應該使用Cache。
你能很輕易的使用工具(如Borland的Optimizeit)或壓力測試發現這些問題, 一旦發現,就能夠立即被糾正,但是很多Java的性能問題隱藏得更深,難於修改源碼就能糾正,如程序組件的接口設計。
現在我們倡導面向對象的組件可復用設計,無疑這樣設計的優點是巨大的, 但是也要注重到對性能的影響。
一個java性能設計原則是,避免不必要的對象創建,對象的創建是非常耗時的, 所以你要避免不必要的臨時或過多的對象創建,
String是程序中最主要創建的對象,因為String是不變的,假如String長度被修改 將導致String對象再次創建,所以對性能有所注重的一般人就是盡量回避使用String, 但是這幾乎是不可能的。
接口參數設計
舉例 MailBot:
MailBot郵件系統的有一個Header數據,它是character buffer,需要對這個character buffer 進行分析比較,那麼你要做一個類Matcher,在這個類中你將Header數據讀入然後配比,一個不好的做法是:
public class BadRegEXPMatcher {
public BadRegExpMatcher(String regExp);
/** Attempts to match the specified regular expression against the input text, returning the matched text if possible or null if not
*/
public String match(String inputText);
}
這個BadRegExpMatche要求入口參數是String ,那麼假如MailBot要調用他,必須自己做一個 character buffer到String的轉換:
BadRegExpMatcher dateMatcher = new BadRegExpMatcher(...);
while (...) { ...
//產生新的String
String headerLine = new String(myBuffer, thisHeaderStart, thisHeaderEnd-thisHeaderStart);
String result = dateMatcher.match(headerLine);
if (result == null) { ... }
}
很明顯,這裡這個由於接口不一致導致了多余的對象String headerline的創建,這是不能答應的, 應該將Matcher的接口設計成能夠接納character buffer,當然為通用性,也應該提供String的 接口參數:
class BetterRegExpMatcher {
public BetterRegExpMatcher(...);
/** 提供多個接口參數的match方法
Provide matchers for multiple formats of input -- String,
character array, and subset of character array. Return
-1 if no match was made; return offset of match start if
a match was made. */
public int match(String inputText);
public int match(char[] inputText);
public int match(char[] inputText, int offset, int length);
/** Get the next match against the input text, if any */
public int getNextMatch();
public int getMatchLength();
public String getMatchText();
}
很明顯BetterRegExpMatcher的運行速度將比前面BadRegExpMatcher運行速度快。
因為在你已經寫好代碼的情況下,你比較難於更改一個類的接口參數,那就應該在寫程序之前多 多考慮你這些接口參數的類型設定,最好有一個通盤的接口類型規定。
減少對象的創建
臨時對象是那些有很短的生命周期,通常服務一些非十分有用的目標,程序員通常使用臨時對象作為 數據混合包傳送或者返回,為避免上述示例哪些轉換接口對象的構造,你應該巧妙的避免創造這些臨時對象,以防止給你的程序留下性能的陰影。
上述示例說明性能問題在於String對象,但是String在對象創建中又是如此的普遍,String是不變的,一旦賦值,就不會變化,不少程序員 認為不變的東西總是會導致壞的性能,其實它並不是這麼簡單,實際上,性能好壞在於你如何使用這個東西。
對於經常需要變化的String,很明顯使用Stringbuffer來代替。
舉例:
看下面兩種實現:
public class Component {
...
protected Rectangle myBounds;
public Rectangle getBounds() { return myBounds; }