一般來說,最好能重用對象而不是在每次需要的時候就創(chuàng)建一個相同功能的新對象捶箱。重用方式既快速坑鱼,又流行膘流。如果對象是不可變的絮缅,他始終可以被重用。
反面例子:
String S = new String("dali");
該語句每次執(zhí)行的時候都創(chuàng)建一個新的String實例呼股,但是這些創(chuàng)建對象的動作全部都是不必要的耕魄。
改進后:
String s = "dali";
這個版本只用了一個String實例,而不是每次執(zhí)行的時候都創(chuàng)建一個新的實例彭谁。而且吸奴,它可以保證,對于所有在同一臺虛擬機中運行的代碼缠局,只要他們包含相同的字符串字面常量则奥,該對象就會被重用。
除了重用不可變的對象之外狭园,也可以重用那些已知不會被修改的可變對象逞度。
public class Person{
? ? ?private ?final Date ?birthDate;
? ? ?public ?Person(Date birthDate){
? ? ? ? ? ? ? ?this.birthDate=new Date(birthDate.getTime());
? ? ?}
? ? ?public boolean isBabyBoomer(){
? ? ? ? ? ? ?Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
? ? ? ? ? ? ?gmtCal.set(1946,Calendar.JANUARY,1,0,0,0);
? ? ? ? ? ? ?Dateboom Start = gmtCal.getTime();
? ? ? ? ? ? ?gmtCal.set(1965,Calendar.JANUARY,1,0,0,0);
? ? ? ? ? ? ?DateboomEnd=gmtCal.getTime();
? ? ? ? ? ? ?return birthDate.compareTo(boomStart)>=0&&
? ? ? ? ? ? ? ? ? ? ? ? ? ?birthDate.compareTo(boomEnd)<0;
? ? ? ? }
}
isBabyBoomer每次調(diào)用的時候,都會創(chuàng)建一個Calendar妙啃、一個TimeZone和兩個Date實例档泽,這是不必要的。
正確方式:
public class Person{
? ? ?private final Date birthDate;
? ? ?public Person(DatebirthDate){
? ? ? ? ? ? ? ?this.birthDate=newDate(birthDate.getTime());
? ? ? }
? ? ? private static final Date BOOM_START;
? ? ? private static final Date BOOM_END;
? ? ? static{
? ? ? ? ? ? ?Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
? ? ? ? ? ? ?gmtCal.set(1946,Calendar.JANUARY,1,0,0,0);
? ? ? ? ? ? ?BOOM_START=gmtCal.getTime();
? ? ? ? ? ? ?gmtCal.set(1965,Calendar.JANUARY,1,0,0,0);
? ? ? ? ? ? ?BOOM_END=gmtCal.getTime();
? ? ? ?}
? ? ? public boolean isBabyBoomer(){
? ? ? ? ? ? ? ?return ?birthDate.compareTo(BOOM_START)>=0&&
? ? ? ? ? ? ? ? ? ? ? birthDate.compareTo(BOOM_END)<0;
? ? ? ? }
}
這種只在初始化時候創(chuàng)建Calendar揖赴、TimeZone和Date實例一次馆匿,而不是在每次調(diào)用isBabyBoomer的時候創(chuàng)建這些實例。如果isBabyBoomer方法被頻繁地調(diào)用燥滑,這種方法將會顯著地提高性能渐北。
避免進行隱式裝箱
自動裝箱是Java5引入的一個特性,即自動將原始類型的數(shù)據(jù)轉(zhuǎn)換成對應(yīng)的引用類型铭拧,比如int轉(zhuǎn)為Integer等赃蛛。
這種特性,在編碼時稍有不注意就可能創(chuàng)建了不必要的對象了搀菩。
反面例子:
Integer?sum?=?0;
for?(int?i?=?1000;?i?<5000;?i++)?{
? ? ? ? sum?+=i;
}
上面的代碼sum+=i可以看成sum =sum+i呕臂,但是+不適用于Integer對象,首先sum進行自動拆箱操作肪跋,進行數(shù)值相加操作歧蒋,最后在自動裝箱轉(zhuǎn)換成Integer對象。其內(nèi)部實現(xiàn)如下:
int?result?=?sum.intValue()?+?i;
Integer?sum?=?new?Integer(result);
由于這里聲明的sum為Integer類型州既,在上面的循環(huán)中會創(chuàng)建將近4000個無用的Integer對象谜洽,在這個循環(huán)中,會降低程序的性能并且加重了垃圾回收的工作量吴叶。因此在我門編程時阐虚,需要注意到這一點,正確地聲明 變量類型蚌卤,避免因為自動裝箱引起的性能問題实束。
謹慎選用容器
java提供了很多編輯的容器集合來組織對象奥秆。比如ArrayList,HashMap等磕洪。
容器雖然使用起來方便吭练,但存在一些問題,就是他們會自動擴容析显,這其中不是創(chuàng)建新的對象鲫咽,而是創(chuàng)建一個更大的容器對象。這就意味著將占用更大的內(nèi)存空間谷异。
以HashMap為例分尸,當我們put key和value時,會檢測是否需要擴容歹嘹,如需要則雙倍擴容箩绍。
public?V?put(K?key,?V?value)?{
? ? ?if?(key?==?null)
? ? ? ? ? ?return?putForNullKey(value);
? ? int?hash?=?hash(key.hashCode());
? ? int?i?=?indexFor(hash,?table.length);
? ?for?(Entry?e?=?table[i];?e?!=?null;?e?=?e.next)?{
? ? ? ?Object?k;
? ? ? ?if?(e.hash?==?hash?&&?((k?=?e.key)?==?key?||?key.equals(k)))?{
? ? ? ? ? ?V?oldValue?=?e.value;
? ? ? ? ? ?e.value?=?value;
? ? ? ? ? ?e.recordAccess(this);
? ? ? ? ? ?return?oldValue;
? ? ? ?}
}
modCount++;
addEntry(hash,?key,?value,?i);
return?null;
}
void addEntry(int?hash,?K?key,?V?value,?int?bucketIndex)?{
Entry?e?=?table[bucketIndex];
? ? ? ? table[bucketIndex]?=?new?Entry(hash,?key,?value,?e);
? ? ? ? if?(size++?>=?threshold)
? ? ? ? ? ? ?resize(2?*?table.length);
}
建議:預(yù)估一個較大的容量值,避免多次擴容
? ? ? 不要錯誤地認為本條碼所介紹的內(nèi)容暗示著“創(chuàng)建對象的代價非常昂貴尺上,我們應(yīng)該要盡可能地避免創(chuàng)建對象”材蛛。相反,由于小對象的構(gòu)造器只做很少量的顯式工作怎抛,所以卑吭,小對象的創(chuàng)建和回收動作是非常廉價的,特別是在現(xiàn)代的JVM實現(xiàn)上更是如此马绝。通過創(chuàng)建附加的對象豆赏,提升程序的清晰性、簡潔性和功能性富稻,這通常是件好事掷邦。