Effective Java Item6 避免不必要的創建物件
整理Effective Java書中Item 6: Avoid creating unnecessary objects心得筆記
主旨
如果是不可變的物件(Immutable object),重複使用該物件可以讓效能以及資源控制更好,例如之前提到的靜態工廠方法Boolean.valueOf(String)
。
點出問題
- 底層封裝了處理方式
問題在於很多開發者不知道其實自己已經不自覺創了很多物件,尤其是java開發者在現在調用api便利的情況下,也沒有去探究底層原理,積少成多造成系統壓力。下面範例判斷字符串是否為有效的羅馬數字。
public class StringUtils {
static boolean isNumeral(String s) {
return s.matches("[0-9]+");
}
}
看起來沒甚麼問題,使用上也正確,但卻有浪費資源問題,你也不知道自己有創建物件,而且是不斷的創建這個昂貴的對象。沿著matches原始碼往下追,最後可以找到new Pattern(regex, 0);
每次使用一次創建一次隨即就gc。
public static Pattern compile(String regex) {
return new Pattern(regex, 0);
}
為了解決問題,將regex Pattern編譯為 Pattern 實例(它是不可變的),作為class初始化的一部分,緩存它,並在每次調用 isRomanNumeral 方法時重複使用同一個實例,這樣就不用每次都創建Pattern。這裡效能大幅提升。
public class StringUtils {
private static final Pattern NUMBER = Pattern.compile("[0-9]+");
static boolean isNumeral(String s) {
return NUMBER.matcher(s).matches();
}
}
2.自動裝箱封裝了處理方式
Autoboxing blurs but does not erase the distinction between primitive and boxed primitive types.
static long sum() {
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++)
sum += i;
return sum;
}
這裡也是一個非常不明顯的地方,每次執行sum += i;
都會創建Long的實例。將Long sum = 0L;
改為long sum = 0L;
可以大幅提高性能,這個是一個很容易犯的錯誤。有興趣的可以跑看看下面範例感受一下性能差距。
static long sum() {
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++)
sum += i;
return sum;
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
sum();
long endTime = System.currentTimeMillis();
System.out.println("That took " + (endTime - startTime) + " milliseconds");
}
小結
讀完這小節可以回去看看自己過去開發的專案,馬上就可以找到不少類似的情況,慢慢修正它吧。