Java 可變對象和不可變對象

1、簡單定義   

        不可變對象(Immutable Objects)即對象一旦被建立它的狀態(對象的數據,也即對象屬性值)就不能改變,反之即爲可變對象(Mutable Objects)。

     不可變對象的類即爲不可變類(Immutable Class)。Java平臺類庫中包含許多不可變類,如String、基本類型的包裝類、BigIntegerBigDecimal等。 html

2、優缺點

     不可變對象有不少優勢: java

  • 構造、測試和使用都很簡單
  • 線程安全且沒有同步問題,不須要擔憂數據會被其它線程修改
  • 當用做類的屬性時不須要保護性拷貝
  • 能夠很好的用做Map鍵值和Set元素
    不可變對象最大的缺點就是建立對象的開銷,由於每一步操做都會產生一個新的對象。

3、編寫不可變類

    能夠遵守如下幾點來編寫一個不可變類: 安全

  • 確保類不能被繼承 - 將類聲明爲final, 或者使用靜態工廠並聲明構造器爲private
  • 聲明屬性爲private 和 final
  • 不要提供任何能夠修改對象狀態的方法 - 不只僅是set方法, 還有任何其它能夠改變狀態的方法
  • 若是類有任何可變對象屬性, 那麼當它們在類和類的調用者間傳遞的時候必須被保護性拷貝
    代碼-1:
import java.util.Date;
/**
 * Planet是一個不可變類,由於當它構造完成以後沒有辦法改變它的狀態
 */
public final class Planet {
    /**
     * 聲明爲final的基本類型數據老是不可變的
     */
    private final double fMass;
    /**
     * 不可變的對象屬性 (String對象不可變)
     */
    private final String fName;
    /**
     * 可變的對象屬性. 在這種狀況下, 這個可變屬性只能被這個類改變。
     * (在其它狀況下, 容許在原生類外部改變一個屬性是頗有意義的;
     * 這種狀況就是當屬性做爲其它地方建立的一個對象引用)
     */
    private final Date fDateOfDiscovery;
    public Planet(double aMass, String aName, Date aDateOfDiscovery) {
        fMass = aMass;
        fName = aName;
        //建立aDateOfDiscovery的一個私有拷貝
        //這是保持fDateOfDiscovery屬性爲private的惟一方式, 而且保護這個
        //類不受調用者對於原始aDateOfDiscovery對象所作任何改變的影響
        fDateOfDiscovery = new Date(aDateOfDiscovery.getTime());
    }
    /**
     * 返回一個基本類型值.
     *
     * 調用者能夠隨意改變返回值,可是不會影響類內部。
     */
    public double getMass() {
        return fMass;
    }
    /**
     * 返回一個不可變對象
     *
     * 調用者獲得內部屬性的一個直接引用. 因爲String是不可變的因此沒什麼影響
     */
    public String getName() {
        return fName;
    }
// /**
// * 返回一個可變對象 - 不是一個好的方式.
// *
// * 調用者獲得內部屬性的一個直接引用. 這一般很危險,由於Date對象既能夠
// * 被這個類改變也能夠被它的調用者改變.即,類再也不對fDate擁有絕對的控制。
// */
// public Date getDateOfDiscovery() {
// return fDateOfDiscovery;
// }
    /**
     * 返回一個可變對象 - 好的方式.
     *
     * 返回屬性的一個保護性拷貝.調用者能夠任意改變返回的Date對象,可是不會
     * 影響類的內部.爲何? 由於它們沒有fDate的一個引用. 更準確的說, 它們
     * 使用的是和fDate有着相同數據的另外一個Date對象
     */
    public Date getDateOfDiscovery() {
        return new Date(fDateOfDiscovery.getTime());
    }
    /**
     * 測試方法
     * @param args
     */
    public static void main(String[] args) {
        Planet planet = new Planet(1.0D, "earth", new Date());
        Date date = planet.getDateOfDiscovery();
        date.setTime(111111111L);
        System.out.println("the value of fDateOfDiscovery of internal class : " + planet.fDateOfDiscovery.getTime());
        System.out.println("the value of date after change its value : " + date.getTime());
    }

     運行結果以下: 測試

     the value of fDateOfDiscovery of internal class : 1393943752205
     the value of date after change its value : 111111111

      因而可知Planet類的屬性fDateOfDiscovery在對象構造完成以後就沒有再改變。
spa

      在《Effective Java》一書中, Joshua Bloch提出了一個強制性的建議 : 線程

     "類應該是不可變的,除非有很好的理由讓它是可變的....若是一個類不能設計爲不可變的,也要儘量的限制它的可變性." 設計

     BigDecimal從技術上講不是不可變的, 由於它沒有聲明爲final. code

4、使用場景

      不可變類最適合表示抽象數據類型(如數字、枚舉類型或顏色)的值。Java 類庫中的基本數據類型的包裝類(如Integer 、 Long 和 Float )都是不可變的,其它數字類型(如 BigInteger 和 BigDecimal )也是不可變的。表示複數或任意精度的有理數的類將比較適合設計爲不可變類。甚至包含許多離散值的抽象類型(如向量或矩陣)也很適合設計成不可變類,這取決於你的應用程序。 orm

       另外一個適合用不可變類實現的好示例就是 事件 。事件的生命期較短,並且經常會在建立它們的線程以外的線程中消耗,因此使它們成爲不可變的是利大於弊。大多數 AWT 事件類都沒有 嚴格的 做爲不可變類來實現。一樣地,在 通訊系統的 組件間 進行 消息傳遞,將消息對象設計成不可變的是明智的。

5、參考資料

    Effective Java htm

      http://www.ibm.com/developerworks/library/j-jtp02183/

      http://www.javapractices.com/topic/TopicAction.do?Id=29

      http://www.javaworld.com/article/2077362/core-java/mutable-or-immutable.html

      http://www.javalobby.org/articles/immutable/

相關文章
相關標籤/搜索