effective java 筆記3

來源:互聯網
上載者:User

from:http://blog.csdn.net/ilibaba/archive/2009/02/06/3866657.aspx

NO.18 優先考慮靜態成員類

嵌套類只為它的外圍類提供服務。

嵌套類分為四種:靜態成員類、非靜態成員類、匿名類和局部類(後面三種稱為內部類),如果希望一個嵌套類的執行個體可以獨立於它的外部類的執行個體而存在,則這個嵌套類應該設定成靜態成員類,但是靜態成員類不能訪問非靜態外圍類對象。非靜態內部類擁有一個指向外圍對象的引用,所以可以訪問外圍類的所有成員,包括被聲明為私人的成員。

匿名類的應用情境:

①建立一個函數對象;

②建立過程對象;例如Thread、Runnable、TimeTask

③在靜態Factory 方法的內部使用;

④在複雜的型別安全枚舉類型(它要求為每個執行個體提供單獨的子類)中,用於公有的靜態final域的初始化器中;

總結:四種不同的嵌套類都有自己不同的用途,如果一個嵌套類需要在單個方法之外仍然是可見的,或者它太長,不適合放在一個方法內部,那麼應該使用成員類。如果成員類的每個執行個體都需要一個指向其外圍執行個體的引用,則把成員類做成非靜態;否則就做成靜態。假設一個嵌套類屬於一個方法的內部,如果你只需要在一個地方建立它的執行個體,並且已經有了一個預先存在的類型可以說明這個類的特徵,則把它做成匿名類;否則就做成局部類。

 

註:以下幾點(NO.19 ~ NO.22)是講解關於C語言結構的替代方案,由於本人只從事java的開發,所以跳過此章節,為了保證筆記的完整性,從其他地方轉載NO.19 ~ NO.22的筆記。

NO.19 用類代替結構
     JAVA剛面世的時候,很多C程式員都認為用類來代替結構現在太複雜,代價太大了,但是實際上,如果一個JAVA的類退化到只包含一個資料域的話,這樣的類與C語言的結構大致是等價的。
      比方說下面兩個程式片段:

  1. class Point 
  2. private float x; 
  3. private float y; 

      實際上這段代碼和C語言的結構基本上沒什麼區別,但是這段代碼恐怕是眾多OO設計Fans所不齒的,因為它沒有體現封裝的優異性,沒有體現物件導向設計的優點,當一個域被修改的時候,你不可能再採取任何輔助的措施了,那我們再來看一看採用包含私人域和共有存取方法的OO設計程式碼片段:

  1. class Point 
  2. private float x; 
  3. private float y; 
  4. public Point(float x,float y) 
  5.   { 
  6. this.x=x; 
  7. this.y=y; 
  8.   } 
  9. public float getX(){retrun x;} 
  10. public float getY(){return y;} 
  11. public void setX(float x){this.x=x;} 
  12. public void setY(float y){this.y=y;} 

        單從表面上看,這段代碼比上面那個多了很多行,還多了很多函數,但是仔細想一下,這樣的OO設計,似乎更人性化,我們可以方面的對範圍進行提取,修改等操作,而不直接和範圍發生關係,這樣的代碼不僅讓人容易讀懂,而且很安全,還吸取了物件導向程式設計的靈活性,試想一下,如果一個共有類暴露它的範圍,那麼想要在將來的版本中進行修改是impossible的,因為共有類的客戶代碼已經遍布各處了。
需要提醒一點的是,如果一個類是包級私人的,或者是一個私人的嵌套類,則直接暴露其範圍並無不妥之處。

 

NO.20 用類層次來代替聯合
我們在用C語言來進行開發的時候,經常會用到聯合這個概念,比如:

  1. typedef struct{ 
  2. double length; 
  3. double width;   
  4. }rectangleDimensions_t; 

那我們在JAVA裡面沒有聯合這個概念,那我們用什麼呢?對!用繼承,這也是JAVA最吸引我的地方之一,它可以使用更好的機制來定義耽擱資料類型,在 Bruce Eckel的Thinking in java裡面也多次提到了一個和形狀有關的例子,我們可以先籠統的定義一個抽象類別,即我們通常所指的超類,每個操作定義一個抽象的方法,其行為取決於標籤的值,如果還有其他的操作不依賴於標籤的值,則把操作變成根類(繼承的類)中的具體方法。
這樣做的最重要的優點是:類層次提供了類型的安全性。其次代碼非常明了,這也是OO設計的優點。而且它很容易擴充,即使是面向多個方面的工作,能夠同樣勝任。最後它可以反映這些類型之間本質上的層次關係,從而允許更強的靈活性,以便編譯時間類型檢查。

 

NO.21 用類來代替enum結構
Java程式設計語言提出了型別安全枚舉的模式來替代enum結構,它的基本思想很簡單:定義一個類來代表枚舉類型的單個元素,並且不提供任何公有的建構函式,相反,提供公有靜態final類,使枚舉類型中的每一個常量都對應一個域。 例如:

public class Suit{

    private final String name;

    private Suit(String name){this.name=name;}

    public String toString(){return name;}

    public static final Suit CLUBS=new Suit(“clubs”);

    public static final Suit DIAMONDS=new Suit(“diamonds”);

}

建構函式是私人的,客戶沒有辦法建立或者擴充這個類。所以除了通過這些公有的靜態final域匯出的Suit對象之外,不會有其他對象存在,即使這個類沒有被申明為final,也沒有辦法對它擴充,因為子類的建構函式必須調用超類的建構函式。
      型別安全枚舉類型的一個缺點是,裝載枚舉類的和構造常量對象時,需要一定的時間和空間開銷,除非是在資源很受限制的裝置比如蜂窩電哈和烤麵包機上,否則在實際中這個問題不會被考慮。
     總之,型別安全枚舉類型明顯優於int類型,除非實在一個枚舉類型主要被用做一個集合元素,或者主要用在一個資源非常不受限的環境下,否則型別安全枚舉類型的缺點都不成問題,依次,在要求使用一個枚舉類型的環境下,我們首先應考慮型別安全枚舉類型模式。

 

NO.22 用類和介面來代替函數指標
眾所周知,JAVA語言和C的最大區別在於,前者去掉了指標,小生第一次接觸JAVA的時候覺得好不習慣,因為突然一下子沒了指標,覺得好不方面啊,C語言的精髓在於其指標的運用,而JAVA卻把它砍掉了,讓人好生鬱悶,不過隨著時間的推移,我漸漸明白了用類和介面的應用也同樣可以提供同樣的功能,我們可以直接定義一個這樣一個類,他的方法是執行其他方法上的操作,如果一個類僅僅是匯出這樣一個方法,那麼它實際上就是一個指向該方法的指標,舉個例子:

  1. class StringLengthComprator{ 
  2. public int compare(String s1,String s2) 
  3. return s1.length()-s2.length(); 

這個類匯出一個帶兩個字串的方法,它是一個用於字串比較的具體策略。它是無狀態的,沒有域,所以,這個類的所有執行個體在功能上都是等價的,可以節省不必要的對象建立開銷。但是我們不好直接把這個類傳遞給使用者使用,因為使用者無法傳遞任何其他的比較策略。相反,我們可以定義一個介面,即我們在設計具體策略類的時候還需要定義一個策略介面:

  1. public interface Comparator{ 
  2. public int compare(Object o1,Object o2); 

具體的策略類往往使用匿名類聲明。
      在JAVA中,我們為了實現指標的模式,聲明一個介面來表示該策略,並且為每個具體策略聲明一個實現了該介面的類,如果一個具體策略只被使用一次的話,那麼通常使用匿名類來聲明和執行個體化這個具體策略類,如果一個策略類反覆使用,那麼它的類通常是一個私人的的靜態成員類。

NO.23 檢查參數的有效性

非公有的方法我們應該用斷言的方法來檢查它的參數,而不是使用通常大家所熟悉的檢查語句來檢測。如果我們使用的開發平台是JDK1.4或者更進階的平台,我們可以使用assert結構;否則我們應該使用一種臨時的斷言機制。

有些參數在使用過程中是先儲存起來,然後在使用的時候再進行調用,就必須做好檢查工作,否則程式可能會拋出一些異常讓你摸不著頭腦(如常見的null 指標異常),也不能馬上定位問題的所在位置,建構函式正是這種類型的一種體現,所以我們通常對建構函式參數的有效性檢查是非常仔細的。

總之,當編寫一個方法或者建構函式的時候,應該考慮對應它的參數有哪些限制,並且要把這些限制寫到文檔中,在方法體的起始處,通過顯示的檢查來實施這些限制。

 

NO.24 需要時使用保護性拷貝

假設類的使用者會盡一切手段來破壞這個類的約束條件,在這樣的前提下,你必須保護性地設計程式。面對客戶的不良行為時仍然能保持健壯性的類。
對於一個非可變類,可以考慮對其建構函式的可變參數採用保護性拷貝,如

  1. public period(Date start, Date end){ 
  2. this.start = new Date(start.getTime()); 
  3. this.end = new Date(start.getTime()); 
  4. // 接著做其他邏輯(保護性拷貝要在其他邏輯之前進行,並且有效性檢查是針對拷貝後的對象,而不是原始對象)

對擷取參數的get方法也要採用clone的方式返回,如:

  1. public Date getStart(){ 
  2. return (Date)start.clone(); 

保護性拷貝是在檢查參數有效性之前進行的,檢查的是拷貝後的對象,避免另外一個線程改變原始的參數對象。

記住非零長度的數組總是可變的,盡量使用非可變的對象作為內部組件,這樣就不必關心保護性拷貝問題.

 

NO.25 謹慎設計方法的原型

1、謹慎選擇方法的名字

     ① 選擇易於理解的,並且與同一個包中的其他名字風格一致;

     ② 選擇與福士認可的名字一致;

2、不要過於追求提供便利的方法。過多的方法會增加類的學習和使用成本,只有當一個操作被用得非常頻繁的時候,才考慮為他提供一個快捷方法。

3、避免過長的參數列表。太長的參數不便於使用者使用,尤其是參數類型相同的時候,很容易產生參數傳遞錯誤的問題。避免此類錯誤的方法:

     ① 可以把一個方法分解成多個方法;

     ② 可以建立一個輔助類(helper class)。將參數組織成一個類作為參數傳入;

4、對於型別參數,優先使用介面,而不是類。如參數為Map的時候,該方法可以接收Hashtable、HashMap、TreeMap等類型的參數。

5、謹慎使用函數對象

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.