不可變類的實例的狀態不會變化,這樣的實例可以安全地被其他與之關聯的對象共享,還可以安全地被多個線程共享。為了節省內存空間,優化程序的性能,應該盡可能地重用不可變類的實例,避免重復創建具有相同屬性值的不可變類的實例。
在JDK 1.5的基本類庫中,對一些不可變類,如Integer類做了優化,它具有一個實例緩存,用來存放程序中經常使用的Integer實例。JDK 1.5的Integer類新增了一個參數,為int類型的靜態工廠方法valueOf(int i),它的處理流程如下:
if(在實例緩存中存在取值為i的實例)
直接返回這個實例
else{
用new語句創建一個取值為i的Integer實例
把這個實例存放在實例緩存中
返回這個實例
}
在以下程序代碼中,分別用new語句和Integer類的valueOf(int i)方法來獲得Integer實例。
Integer a=new Integer(10);
Integer b=new Integer(10);
Integer c=Integer.valueOf(10);
Integer d= Integer.valueOf(10);
System.out.println(a==b); //打印false
System.out.println(a==c); //打印false
System.out.println(c==d); //打印true
以上代碼共創建了3個Integer對象,每個new語句都會創建一個新的Integer對象。而Integer.valueOf(10)方法僅在第一次被調用時,創建取值為10的Integer對象,在第二次被調用時,直接從實例緩存中獲得它。由此可見,在程序中用valueOf()靜態工廠方法獲得Integer對象,可以提高Integer對象的可重用性。
到底如何實現實例的緩存呢?緩存並沒有固定的實現方式,完善的緩存實現不僅要考慮何時把實例加入緩存,還要考慮何時把不再使用的實例從緩存中及時清除,以保證有效合理地利用內存空間。一種簡單的實現是直接用Java集合來作為實例緩存。
下面的例程,它擁有實例緩存和相應的靜態工廠方法valueOf()。Name類的實例緩存中可能會加入大量Name對象,為了防止耗盡內存,在實例緩存中存放的是Name對象的軟引用(SoftReference)。如果一個對象僅僅持有軟引用,Java虛擬機會在內存不足的情況下回收它的內存。
例程11-12 Name.Java
import Java.util.Set;
import Java.util.HashSet;
import Java.util.Iterator;
import Java.lang.ref.*;
public class Name {
…
//實例緩存,存放Name對象的軟引用
private static final Set> names=new HashSet>();
public static Name valueOf(String firstname, String lastname){ //靜態工廠方法
Iterator> it=names.iterator();
while(it.hasNext()){
SoftReference ref=it.next();//獲得軟引用
Name name=ref.get();//獲得軟引用所引用的Name對象
if(name!=null&& name.firstname.equals(firstname)&& name.lastname.equals(lastname))
return name;
}
//如果在緩存中不存在Name對象,就創建該對象,並把它的軟引用加入到實例緩存
Name name=new Name(firstname,lastname);
names.add(new SoftReference(name));
return name;
}
public static void main(String args[]){
Name n1=Name.valueOf("小紅","王");
Name n2=Name.valueOf("小紅","王");
Name n3=Name.valueOf("小東","張");
System.out.println(n1);
System.out.println(n2);
System.out.println(n3);
System.out.println(n1==n2); //打印true
}
}
在程序中,既可以通過new語句創建Name實例,也可以通過valueOf()方法創建Name實例。在程序的生命周期中,對於程序不需要經常訪問的Name實例,應該使用new語句創建它,使它能及時結束生命周期;對於程序需要經常訪問的Name實例,那就用valueOf()方法來獲得它,因為該方法能把Name實例放到緩存中,使它可以被重用。
Tips
從例程11-12的Name類也可以看出,在有些情況下,一個類可以同時提供public的構造方法和靜態工廠方法。用戶可以根據實際需要,靈活地決定到底以何種方式獲得類的實例。
另外要注意的是,沒有必要為所有的不可變類提供實例緩存。隨意創建大量實例緩存,反而會浪費內存空間,降低程序的運行性能。通常,只有滿足以下條件的不可變類才需要實例緩存。
1. 不可變類的實例的數量有限。
2.在程序運行過程中,需要頻繁訪問不可變類的一些特定實例。這些實例擁有與程序本身同樣長的生命周期。