不變類的意思就是不會發生變化的類,就是當類的執行個體被建立後,狀態不會發生變化的類。
舉個例子:如果人是一個class,那麼我們中的每一個都是人這個類的具體的instance,如果人這個類只有一個狀態就是生身父母,那麼它就是一個不變類,因為每一個人在出生的那一刹那,生身父母就已經被設定了值,而且終生都不會發生變化。
不變類有什麼好處呢?
1) 不變類是安全執行緒的,由於不變類的狀態在建立以後不再發生變化,所以它可以線上程之間共用,而不需要同步。
2) 不變類的instance可以被reuse
建立類的執行個體需要耗費CPU的時間,當這個執行個體不再被引用時,將會被記憶體回收掉,這時候,又需要耗費CPU的時間。對於不變類而言,一個好處就是可以將常用的執行個體進行緩衝,從而減少了對象的建立。舉個例子,對於布爾型,最常用的便是true and false。JDK中的Boolean類就是一個不變類,並且對這兩個執行個體進行了緩衝。
public final class Boolean implements java.io.Serializable{
/**
* The <code>Boolean</code> object corresponding to the primitive
* value <code>true</code>.
*/
public static final Boolean TRUE = new Boolean(true);
/**
* The <code>Boolean</code> object corresponding to the primitive
* value <code>false</code>.
*/
public static final Boolean FALSE = new Boolean(false);
// 這個方法不會建立新的對象,而是重用已經建立好的instance
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
}
3) 不變類的某些方法可以緩衝計算的結果
hashCode這個方法來自於Object這個類,這個方法用來返回對象的hashCode,主要用於將對象放置到hashtable中時,來確定這個對象的儲存位置。對於一個不變類的執行個體,它的hashCode也是不變的,所以就可以緩衝這個計算的結果,來提高效能,避免不必要的運算,JDK中的String類就是一個例子。
public final class String{
/** Cache the hash code for the string */
private int hash; // Default to 0
public int hashCode() {
int h = hash;
if (h == 0) {
// compute the value
hash = h; // cache the value
}
return h;
}
}
在JDK中, String, the primitive wrapper classes, and BigInteger and BigDecimal都是不變類。
如果一個類是不變類,這個類是不是就不能有改變狀態的方法呢?
答案當然是否定的,String是一個不變類,仍然有replace,replaceAll這樣的方法,而String仍然是一個不變類,那是因為在這些改變狀態的方法中,每次都是新建立一個String對象。
如果大家理解了不變類,那也就不難理解為什麼在做String的concatenate時,應當用StringBuffer而不是用+的操作符。
如何正確使用String呢?
1) 不要用new去建立String對象。
如果使用new去建立String,那麼每次都會建立一個新對象。
public static void main(String[] args) {
String A1 = "A";
String A2 = "A"; // It won't create a new object
checkInstance(A1, A2); // Result: They are same instances
String B1 = new String("A"); // create a new object
String B2 = new String("A"); // creat a new object
checkInstance(B1, B2); // Result: They are different instances
}
private static void checkInstance(String a1, String a2) {
if (a1 == a2) {
System.out.println("They are same instances");
} else {
System.out.println("They are different instances");
}
}
2) 應當用StringBuffer來做串連操作
因為String是一個不變類,那麼在做串連操作時,就會建立臨時對象來儲存中間的運算結果,而StringBuffer是一個mutable class,這樣就不需要建立臨時的對象來儲存結果,從而提高了效能。