一般來說抛虫,最好能重用對象而不是在每次需要的時候就創(chuàng)建一個相同功能的新對象松靡。重用的方式即快速,又流行建椰。如果對象是不可變的雕欺,他就始終可以被重用。
重用不可變對象
我們可以舉一個String類的例子棉姐,String類常量一旦被賦值就不可被改變屠列,就可以始終被重用。
//這是不可取的伞矩,因為每次執(zhí)行這條語句都會創(chuàng)建新的對象
String s1 = new String("hello world");
我們應(yīng)當(dāng)這樣使用下面的方式笛洛。
//java中,字符串常量存在方法區(qū)的字符串常量池中乃坤,方便相同內(nèi)容的字符串復(fù)用
String s1 = "hello world";
對于同時提供了靜態(tài)工廠方法和構(gòu)造器的不可變類苛让,通常可以使用靜態(tài)工廠方法而不是構(gòu)造器湿诊,以避免創(chuàng)建不必要的對象狱杰。例如,靜態(tài)工廠方法Boolean.valueOf(String)要比Boolean(String)要好枫吧。構(gòu)造器每次被調(diào)用的時候都會創(chuàng)建一個新的對象浦旱,而靜態(tài)工廠方法則不會。
除了重用不可變的對象外九杂,也可以重用那些不會被修改的可變對象颁湖。
重用不會被修改的可變對象
典型例子,Date對象例隆。
假如我們現(xiàn)在有一個判斷限定在8月29到8月31日可用的KFC優(yōu)惠券是否可用時
通常我們會:
public Class Coupon{
public boolean isExpire(){
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(2016,Calendar.AUGUST,29,0,0,0);
Date startTime = gmtCal.getTime();
gmtCal.set(2016,Calendar.SEPTEMBER,1,0,0,0);
Date endTime = gmtCal.getTime();
Date now = new Date();
return now.compareTo(startTime) >= 0 &&
now.compareTo(endTime) < 0;
}
}
這種情況下甥捺,每次調(diào)用 isExpire 都需要創(chuàng)建一個Calendar,TimeZone和三個Date對象镀层,而其中除了now Date镰禾,其他都是的值都是不變的。這樣就白白每次多創(chuàng)建了3個對象唱逢。
建議用法
public Class Coupon{
private static final Date START_TIME;
private static final Date END_TIME;
//創(chuàng)建對象時吴侦,會先執(zhí)行靜態(tài)部分。
Static{
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(2016,Calendar.AUGUST,29,0,0,0);
startTime = gmtCal.getTime();
gmtCal.set(2016,Calendar.SEPTEMBER,1,0,0,0);
endTime = gmtCal.getTime();
}
public boolean isExpire(){
Date now = new Date();
return now.compareTo(startTime) >= 0 &&
now.compareTo(endTime) < 0;
}
}
改進(jìn)后的Coupon類只會在初始化的時候創(chuàng)建一個Calendar坞古,TimeZone和兩個Date對象备韧,而不是每次調(diào)用isExpire 的時候。
Java中有很多這方面典型的應(yīng)用痪枫。例如织堂,Map接口的keySet方法分返回該Map對象的Set視圖叠艳,其中包含該Map中所有的鍵(key)。實(shí)際上每次調(diào)用keySet都返回同樣的Set實(shí)例易阳,當(dāng)其中一個返回對象發(fā)生變化的時候附较,其他返回對象都是對同一對象的引用,因此也會發(fā)生變化潦俺。
另外拒课,在JDK 1.5 中,有一種創(chuàng)建多余對象的新方法黑竞,即自動裝箱捕发。
它允許基本類型和對象轉(zhuǎn)換,Example: Integer int
下面有個自動裝箱錯誤的例子:
public static void main(String[] args){
Long sum = 0L;
for( long i - 0 ; i < Integer.MAX_VALUE; i++){
sum += i;
}
}
由于sum使用的是Long類型很魂,如此每次計算的時候扎酷,i會由long裝箱成Long,這樣一來會創(chuàng)建N個Long對象再進(jìn)行計算,速度自然慢很多遏匆。通過將sum的類型由Long轉(zhuǎn)換為long法挨,書中給出的數(shù)據(jù)是運(yùn)行時從43秒降到6.8秒。
因此幅聘,要優(yōu)先使用基本類型而不是包裝類凡纳,要當(dāng)心無意識的自動裝箱。
總結(jié)
不要錯誤的認(rèn)為"創(chuàng)建對象的代價非常昂貴帝蒿,我們盡可能的不要創(chuàng)建對象"
相反荐糜,小的對象的構(gòu)造器只做很少量的顯示工作。
所以葛超,小對象的創(chuàng)建和回收是非常廉價的暴氏,特別是現(xiàn)代JVM上更是如此(因為JVM不斷優(yōu)化)。
通過創(chuàng)建附加的對象绣张,提升程序的清晰性答渔、簡潔性和功能性,這通常是件好事侥涵。
反之沼撕,通過維護(hù)自己的對象池來避免創(chuàng)建對象并不是一種好的做法,除非池中的對象時非常重量級的
重量級對象的典型就是數(shù)據(jù)庫連接對象芜飘,從而有了數(shù)據(jù)庫連接池(因為建立數(shù)據(jù)庫連接的代價是非常昂貴的务豺,因此重用這些對象非常有意義)。
但是嗦明,一般而言冲呢,維護(hù)自己的對象池必定會把代碼弄得很亂,同時增加內(nèi)存占用,并且還會損害性能【赐兀現(xiàn)代JVM實(shí)現(xiàn)具有高度優(yōu)化的垃圾回收器,其性能很容易就會超過輕量級對象池的性能裙戏。
總之乘凸,對于不可變類以及可變的但是一旦建立后就不會改變的對象可以重用對象。在其他情況下累榜,對于重量級對象可考慮重用营勤,對其他對象要靈活創(chuàng)建,不要因為維護(hù)對象池壹罚,避免創(chuàng)建新的對象而造成額外負(fù)擔(dān)葛作,得不償失。