Java之避免建立不必要的物件
我曾經接手過一個工程,裡面有一段讓我頭暈目眩的程式碼,展示如下:
String str1 = ""; for (int i = 0; i < list.size(); i++) { str1 += list.get(i); if (i < list.size() - 1) { str1 += ","; } }
這段程式碼大概就是想要拼成一個很長很長的String罷了,看起來邏輯也很簡單,還用上了foreach,但是我還是要說讓我頭暈目眩,原因就在於String是一個 不可變類 。
我們來看看String的原始碼就知道了什麼叫做不可變類:

String
這種類有一個典型的特點就是不可繼承,對於String的任何拼接操作,實際上都是在新建物件,上面這段程式碼實際上就建立了list.size()+1個String物件。
那麼這就是一段很不優雅的程式碼了,至少一個資深的程式設計師永遠不會在迴圈中做String的拼接的。這其實也就是《Effective Java》這本書的第六條提到的所謂“避免建立不必要的物件”了。
這一條裡舉了一個類似的例子:
//String是不可變類,XXX實際上已經建立好了一個物件,此時new只會再次建立一個物件 //不可變類可以被複用 String str = new String("XXX");
這個時候可以用StringBuilder了,如下程式碼片段:
StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < list.size(); i++) { stringBuilder.append(list.get(i)); if (i < list.size()-1) { stringBuilder.append(","); } } //這是最討厭的一步,需要把StringBuilder轉成String才能繼續使用 String s = stringBuilder.toString();
上面這段程式碼還是不美好,因為額外生成了一個StringBuilder物件,這不是我想要的,這也算是額外的物件了。
這時要是有個靜態工廠方法就好了。還好這世界還有Google,提供的Guava裡面就有很多很好的工具:
String str = Joiner.on(",").join(list);
這就好了,不會生成額外的物件。
這只是一個小例子,也許讀到這裡的人會說,現在的計算機硬體已經很強大了,多核CPU,大記憶體,SSD硬碟,根本不在於這點細枝末節的提升。那麼這個時候就應該好好看看資料庫連線池的設計了。
我們知道,資料庫建鏈的過程實際上是一次TCP建鏈的過程,包括了經典的三次握手協議,在《碼農翻身》這本書裡作者也風趣講了這個協議是多麼的讓人崩潰,但是為了資料庫連線的穩定可靠,必須這麼做。這也就意味著資料庫建鏈的過程是一個昂貴的操作,因此,避免每次連線的時候都去新建一個連結是必要的,也就是說我們需要把建立好的連線物件放在一個容器裡儲存起來,這就是避免建立不必要的物件的思想了。
注意,絕對不是說盡量不要建立物件,不管到什麼時候,一定要強調所謂 不必要的 這個概念。
我記得我上學的時候面向物件這門課的老師講Java的時候說過,Java程式設計師掌握越多的類庫,那麼他的程式設計能力越強大。這句話部分對,因為有的時候,你看著Guava用起來爽,但是沒有思考為什麼爽,為什麼Google要做這麼一件事情,那你也不過是掌握了這個類庫怎麼用而已。
正所謂學而不思則罔。