如何在Java中寫出Immutable的類?java
要寫出這樣的類,須要遵循如下幾個原則:緩存
1)immutable對象的狀態在建立以後就不能發生改變,任何對它的改變都應該產生一個新的對象。安全
2)Immutable類的全部的屬性都應該是final的。併發
3)對象必須被正確的建立,好比:對象引用在對象建立過程當中不能泄露(leak)。性能
4)對象應該是final的,以此來限制子類繼承父類,以免子類改變了父類的immutable特性。this
5)若是類中包含mutable類對象,那麼返回給客戶端的時候,返回該對象的一個拷貝,而不是該對象自己(該條能夠歸爲第一條中的一個特例)spa
固然不徹底遵照上面的原則也可以建立immutable的類,好比String的hashcode就不是final的,但它能保證每次調用它的值都是一致的,不管你多少次計算這個值,它都是一致的,由於這些值的是經過計算final的屬性得來的!線程
下面是一個例子:code
public final class Contacts { private final String name; private final String mobile; public Contacts(String name, String mobile) { this.name = name; this.mobile = mobile; } public String getName(){ return name; } public String getMobile(){ return mobile; } }
咱們爲類添加了final修飾,從而避免由於繼承和多態引發的immutable風險。對象
上面是最簡單的一種實現immutable類的方式,能夠看到它的全部屬性都是final的。
有時候你要實現的immutable類中可能包含mutable的類,好比java.util.Date,儘管你將其設置成了final的,可是它的值仍是能夠被修改的,爲了不這個問題,咱們建議返回給用戶該對象的一個拷貝,這也是Java的最佳實踐之一。下面是一個建立包含mutable類對象的immutable類的例子:
public final class ImmutableReminder{ private final Date remindingDate; public ImmutableReminder (Date remindingDate) { if(remindingDate.getTime() < System.currentTimeMillis()){ throw new IllegalArgumentException("Can not set reminder」 + 「 for past time: " + remindingDate); } this.remindingDate = new Date(remindingDate.getTime()); } public Date getRemindingDate() { return (Date) remindingDate.clone(); } }
上面的getRemindingDate()方法能夠看到,返回給用戶的是類中的remindingDate屬性的一個拷貝,這樣的話若是別人經過getRemindingDate()方法得到了一個Date對象,而後修改了這個Date對象的值,那麼這個值的修改將不會致使ImmutableReminder類對象中remindingDate值的修改。
使用Immutable類的好處:
1)Immutable對象是線程安全的,能夠不用被synchronize就在併發環境中共享
2)Immutable對象簡化了程序開發,由於它無需使用額外的鎖機制就能夠在線程間共享
3)Immutable對象提升了程序的性能,由於它減小了synchroinzed的使用
4)Immutable對象是能夠被重複使用的,你能夠將它們緩存起來重複使用,就像字符串字面量和整型數字同樣。你可使用靜態工廠方法來提供相似於valueOf()這樣的方法,它能夠從緩存中返回一個已經存在的Immutable對象,而不是從新建立一個。
immutable也有一個缺點就是會製造大量垃圾,因爲他們不能被重用並且對於它們的使用就是」用「而後」扔「,字符串就是一個典型的例子,它會創造不少的垃圾,給垃圾收集帶來很大的麻煩。固然這只是個極端的例子,合理的使用immutable對象會創造很大的價值。