本篇筆記對應原書條目6,介紹幾個避免建立沒必要要對象的方法。java
若是一個方法中的一些對象,每次調用都起到相同的做用,那麼就能夠被重用。程序員
好比,咱們不該該用以下的方式來建立一個String對象:數據庫
String str = new String("aaa");
由於當咱們往構造方法裏傳入aaa的時候,其實這個aaa就是一個String實例了。咱們等因而建立了兩個String實例。數組
咱們應該直接這麼寫:安全
String str = "aaa";
根據jdk文檔,上述方式實際上等同於:app
char data[] = {'a', 'a', 'a'}; String str = new String(data);
傳入一個字符數組來建立String,避免了建立重複對象。工具
再舉一個常見的例子[2],咱們有時但願遍歷一個list,將其中的元素存到一個字符串裏,並用逗號分隔。咱們可能會用下面這種最low的辦法:性能
public static String listToString(List<String> list) { String str = ""; for (int i = 0; i < list.size(); i++) { str += list.get(i); if (i < list.size() - 1) { str += ","; } } return str; }
這樣其實在每次+=
的時候都會從新建立String對象,極大地影響了性能。學習
咱們能夠修改一下,採用StringBuilder的方式來拼接list:優化
public static String listToString(List<String> list) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < list.size(); i++) { stringBuilder.append(list.get(i)); if (i < list.size() - 1) { stringBuilder.append(","); } } return stringBuilder.toString(); }
這種方式每次只會生成兩個實例——StringBuilder
和最後返回的String
。
那有沒有更好的方法呢?咱們能夠採用Google Guava的Joiner
,這樣每次只用生成一個實例,以下所示:
public static String listToString(List<String> list) { return Joiner.on(",).join(list); }
有時候咱們提供的API中有一些每次調用都具有相同功能的對象,那麼就能夠把這些對象變成靜態的不可變對象,只需實例化一次便可。好比下面這個相似書中的例子[1]:
public static boolean isNumeral(String s) { return s.matches("^[0-9]*$"); }
這個例子用String.matches()
方法來判斷字符串是否爲數字。每次調用matches()
方法,裏面都會建立一個Pattern
對象,這會對性能形成影響。
由於每次調用isNumeral
實際上都會生成一個功能徹底相同的Pattern
對象,因此咱們能夠把它抽出來,變成一個靜態不可變對象,以下所示:
public static final Pattern NUMBER = Pattern.compile("^[0-9]*$"); public static boolean isNumeral(String s) { return NUMBER.matcher(s).matches(); }
上面咱們談到了一個不可變對象的重用,接下來咱們再看看可變對象的重用。可變對象的重用能夠經過視圖(views)來實現。好比,Map的keySet()
方法就會返回Map對象全部key的Set視圖。這個視圖是可變的,可是當Map對象不變時,在任何地方返回的任何一個keySet都是同樣的,當Map對象改變時,全部的keySet也會相應的發生改變。[1]
自動裝箱容許程序員混用基本類型和包裝類型[1],在二者相計算時,程序會構造出基本類型的包裝類型實例。例如,咱們看書中的這個例子:
private static long sum() { Long sum = 0L; for (long i = 0; i <= Integer.MAX_VALUE; i++) sum += i; return sum; }
這個例子裏的sum += i
處用Long
型和long
型相加,這樣每次都會實例化一個值爲i
的包裝類型,一共實例化了約231個沒必要要的實例,極大地下降了系統的性能。因此咱們在平常開發中,方法內儘可能用基本類型,只在入出參的地方用包裝類型。多留心,切忌無心識地使用到自動裝箱。[1]
若是涉及到對象池的應用,除非池中的對象很是重,相似數據庫鏈接,不然最好不要去本身維護一個對象池,由於這樣會很複雜。另外,有時考慮到系統的安全性,那麼咱們須要進行防護性複製,這個在後面會講到。此時,重複建立對象就是有意義的,由於比起隱含錯誤和安全漏洞,重複建立對象帶來的性能損失是能夠接受的。[1]
這個條目要求咱們平時多審視咱們的代碼,在能不重複建立對象的地方,就不要重複建立。不要無腦寫代碼,而是要多留點心。這是一種很是好的編碼習慣,也能夠爲系統帶來性能上的優點。
本文僅用於學習交流,請勿用於商業用途。轉載請註明出處,若是涉及任何版權問題,請及時與我聯繫,謝謝!