1. 程式人生 > >《effective java》學習筆記(一)

《effective java》學習筆記(一)

以下內容絕大部分來自《effective java》這本書,其中會有本人的一些學習是的想法,本文屬於偽原創

強烈建議大家在讀本文即後續文章時,先了解java的23中設計模式,可以看本人寫的關於設計模式的部落格或者

買本《大話設計模式》看看

第1條:考慮用靜態工廠方法代替構造器

    對於類而言,為了讓客戶端獲取它自身的一個例項,最常用的方法就是提供一個共有的構造器(即類中的構造方法)。

還有一種方法,也應該在每一個程式設計師的工具箱中戰友一席之地。類可以提供一個公有的靜態工廠方法,他只是一個返回類的例項的靜態方法。

下面是一個來自Boolean的簡單示例。這個方法將boolean基本型別值轉換成了一個Boolean物件引用:

   public static Boolean valueOf(boolean b){

        return b?Boolean.TRUE:Boolean.FALSE;

}

注意,靜態工廠方法與設計模式中的工廠方法模式不同,本條目中的靜態工廠方法只是static方法

類可以通過靜態工行方法來提供它的客戶端,而不是通過構造器。提供靜態工廠方法而不是公有的構造器,這樣做具有幾大優勢。

       靜態工廠方法與構造器不同的第一大有時在於,他們有各自的名稱。如果構造器的引數本身沒有確切地描述正被返回的物件,

那麼具有適當名稱的靜態工廠會更容易使用,產生的客戶端程式碼也更容易閱讀。例如,構造方法BigInteger(int,int,Random)

返回的BingInteger可能為素數,如果用名為BigInteger.pprobablePrime的靜態工廠方法來表示,顯然更為清楚。(jdk1.4以上新增)

       一個類只能有一個帶有制定簽名的構造方法。程式設計人員通常知道如何避開這一限制:通過提供兩個構造方法,他們的引數列表

只在引數型別的順序上有所不同。實際上這並不是個好主意。面對這樣的API,使用者永遠也記不住改用哪個構造方法,結果會呼叫

錯誤的構造器。並且,讀到使用了這些構造方法程式碼時,如果沒有參考類的文件,往往不知所云。

       由於靜態工廠方法有名稱,所以他們不收上述的限制。當一個類需要多個帶有相同簽名的構造方法時,就用靜態工廠方法代替

構造方法,並且慎重的選擇名稱以便突出他們之間的區別。

       靜態工廠方法與構造方法不同的第二大優勢在於,不必每次呼叫他們的時候都穿件一個新物件。這使得不可變類可以使用

預先構建好的例項,或者將構建好的例項快取起來,進行重複利用,從而避免不必要的重複物件。Boolean.value(boolean)方法

說明了這項技術:它從來不建立物件。如果程式經常請求建立相同的物件,並且穿件物件的代價很高,則這項技術可以極大地

提升效能。

       靜態工廠方法能夠為重複的呼叫返回相同物件,這樣有助於類總能嚴格控制在某個時刻哪些例項應該存在。這種類被稱作

實力受控的類。編寫例項受控的類有幾個原因。例項受控使得可以確保它是一個Singleton或者是不可例項化的。它還是得不可變

的類可以確保不會存在兩個相等的例項,即當且僅當a==b的時候才有a.equals(b)為true。如果類保證了這一點,它的客戶端就可以

使用==操作符來代替equals(Object)方法,這樣可以提升效能。列舉(enum)型別保證了這一點。

         補充(對於引用型別來說==操作符比較的是兩個類在jvm記憶體中比較的是兩個類的地址是否相同,equals比較的是兩個類的內容

是否相同,想要進一步瞭解可以看我的另一邊博文,equas和==的區別)。

         靜態工廠方法與構造器不同的第三大有時在於,他們可以返回型別的任何子型別的物件。這樣我們在選擇返回物件的類時

就有了更大的靈活性。

        這種靈活性的一種應用是,API可以返回物件,同時又不會使物件的類變成共有的。以這種方式隱藏實現類會使API變得非常簡潔。

這項技術適用於基於介面的框架,因為在這種框架中,介面為靜態工廠方法提供了自然返回型別。介面不能有靜態方法,因此按照慣例,

介面Type的靜態工廠方法被放在一個名為Types的不可例項化的類中。

        例如,Java Collections Framework 的集合介面有32個便利實現,分別提供了不可修改的集合、同步集合等等。幾乎所有這些

實現都通過靜態工廠方法放在一個不可例項化的類中匯出。所有返回物件的類都是非公有的。

        現在的Collections Framework API比匯出32個獨立公有類的那種實現方式要小得多,每種便利實現都對應一個類。這不僅僅是指API數量上的

減少,也是概念意義上的減少。使用者知道,被返回的物件是由相關的介面精確指定的,所以他們不需要閱讀有關的類文件。使用這種靜態

工廠方法時,甚至要求客戶端通過介面來引用被返回的物件,而不是通過它的實現類來引用被返回的物件,這是一種良好的習慣。

        公有的靜態工廠方法所返回的物件的類不僅可以非公有的,而且該類還可以隨著每次呼叫而發生變化,這取決於靜態工廠方法的引數值。

只要是已宣告的返回型別的子型別,都是允許的。為了提升軟體的可維護性和效能,返回物件的類也可能隨著發行版本的不同

而不同。

       髮型版本1.5中引入的類java.util.EnumSet沒有公有的構造器,只有靜態工廠方法。它們返回兩種實現類之一,具體則取決於底層

列舉型別的大小,如果它的元素有64個或者更少,就像大多數列舉型別一樣靜態工廠方法就會返回一個RegalarEumSet例項,用

單個long進行支援,如果列舉型別有65個或者更多元素,工廠就返回JumboEnumSet例項,用long陣列進行支援。

       這兩個實現類的存在對於客戶端來說是不可見的。如果RegularEnumSet不能再給小的列舉型別提供效能優勢,就可能從

未來的髮型版本中將它刪除,不會造成不良的影響。同樣地,如果事實證明對效能有好處,也可能在未來的髮型版本中新增第三

甚至第四EnumSet實現。客戶端永遠不知道也不關心他們從工廠方法中得到物件的類,他們只關心它是EnumSet的某個子類即可。

       靜態工廠返回的物件所屬的類,在編寫包含該靜態工廠方法的類時可以不必存在。這種靈活的靜態工廠方法構成了服務提供者

框架的基礎,例如JDBC API。服務提供者框架是指這樣一個系統:多個服務提供者實現一個服務,系統為服務提供者的客戶端提供

多個實現,並把他們從多個實現中解耦出來。

       服務提供者框架中有三個重要的元件:服務介面,這是提供者實現的,提供者註冊API,這是系統用來註冊實現,讓客戶端訪問它

們的,服務訪問API,是客戶端用來獲取服務的例項的。服務訪問API一般允許但是不要求客戶端制定某種選擇提供者的條件。如果沒有

這樣的規定,API就會返回預設實現的一個例項。服務訪問API是”靈活的靜態工廠“,它構成了服務提供者框架的基礎。

      服務提供者框架的第四個元件是可選的:服務提供者介面,這些提供者負責建立器服務實現的例項。如果沒有服務提供者介面,

實現就按照類名註冊,並通過反射方式進行例項化。對於JDBC來說,實現就按照類名稱註冊,並通過反射方式進行例項化。對於

JDBC來說,Connection就是它的服務介面,DriverManager.registerDriver是提供者註冊API,DriverManager.getConnection是服務

訪問API,Driver就是服務提供者介面。

      服務提供者框架模式有著無數變種。例如,服務訪問API可以利用介面卡模式,返回比提供者需要的更吩咐的服務介面。下面是一個

簡單的實現,包含一個服務提供者介面和一個預設提供者:

public interface Provider {
    Service newService();
}

public interface Service {
    // Service-specific methods go here
}

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Services {
    private Services() {
    } // Prevents instantiation (Item 4)

    // Maps service names to services
    private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";

    // Provider registration API
    public static void registerDefaultProvider(Provider p) {
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }

    public static void registerProvider(String name, Provider p) {
        providers.put(name, p);
    }

    // Service access API
    public static Service newInstance() {
        return newInstance(DEFAULT_PROVIDER_NAME);
    }

    public static Service newInstance(String name) {
        Provider p = providers.get(name);
        if (p == null)
            throw new IllegalArgumentException(
                    "No provider registered with name: " + name);
        return p.newService();
    }
}
public class Test {
    public static void main(String[] args) {
        // Providers would execute these lines
        Services.registerDefaultProvider(DEFAULT_PROVIDER);
        Services.registerProvider("comp", COMP_PROVIDER);
        Services.registerProvider("armed", ARMED_PROVIDER);

        // Clients would execute these lines
        Service s1 = Services.newInstance();
        Service s2 = Services.newInstance("comp");
        Service s3 = Services.newInstance("armed");
        System.out.printf("%s, %s, %s%n", s1, s2, s3);
    }

    private static Provider DEFAULT_PROVIDER = new Provider() {
        public Service newService() {
            return new Service() {
                @Override
                public String toString() {
                    return "Default service";
                }
            };
        }
    };

    private static Provider COMP_PROVIDER = new Provider() {
        public Service newService() {
            return new Service() {
                @Override
                public String toString() {
                    return "Complementary service";
                }
            };
        }
    };

    private static Provider ARMED_PROVIDER = new Provider() {
        public Service newService() {
            return new Service() {
                @Override
                public String toString() {
                    return "Armed service";
                }
            };
        }
    };
}

靜態工廠方法的第四大優勢在於,在建立引數化型別的時候,他們使程式碼變得更加簡潔。遺憾的是,在呼叫引數化類的構造器時,

即使型別引數很明顯,也必須指明。這通常要求你連線兩次型別引數:

        Map<String,List<String>> m = new HashMap<String List<String>>;

隨著型別引數變得越來越長,越來越複雜,這一實現的說明也很快變得痛苦起來。但是有了靜態工廠方法,編輯器就可以代替你找

到型別引數。這被稱作型別推到。例如,假設HashMap提供了這個靜態工廠:

  public static <K,V> HashMap<K,V> new Instance(){

       return new HashMap<K,V>();

你就可以用下面這句簡潔的程式碼代替上面這段繁瑣的宣告:

Map<String,List<String>> m = HashMap.newInstance();

遺憾的是,java1.6目前還不支援

       靜態工廠方法的主要缺點在於,類如果不含公有的或者受保護的構造器,就不能被子類化。對於公有的靜態工廠方法所返回的

非公有類,也同樣如此。例如,想要將Collections Framework中的任何方便的實現類子類化,這是不可能的。但是這樣也會因禍得

福,因為它鼓勵程式設計師使用複合,而不是繼承。

      靜態工廠方法的第二個缺點在於,它們與其他的靜態方法實際上沒有任何區別。在API文件中,它們沒有像構造器那樣在API文件

中明確標識出來,因此,對於提供了靜態工廠方法二不是構造器的類來說,想要查明如何例項化一個類,這是非常困難的。Javadoc工具

總有一天會注意到靜態工廠方法。同時,你通過在類或者介面註釋中關注靜態工廠,並遵守標準的命名習慣,也可以彌補這一劣勢

     下面是靜態工廠方法的一些慣用名稱:

    valueOf----不太嚴格的講,該方法返回的例項與它的引數具有相同的值。這樣的靜態工廠方法實際上是型別轉化方法。

    of-----valueOf的一種更為簡潔的替代,在EnumSet中使用並流行起來。

     getInstance-----返回的例項是通過方法的引數來描述的,但是不能夠說與引數具有同樣的值。對於Singleton來說,該方法沒有引數

並返回唯一的實現。

     newInstance-----向getInstance一樣,但newInstance能夠確保返回的每個例項都與所有其他例項不同。

     getType----像getInstance一樣,但是在工廠方法處於不同類中的時候使用。Type表示工廠方法返回的物件型別。

      簡而言之,靜態工廠方法和公有構造器都各有好處,我們需要理解它們各自長處。靜態工廠通常更加合適,因此切忌第一反應

就是提供公有的構造器,而不先考慮靜態工廠。

相關推薦

我的Effective Java 學習筆記

學習記錄,也是生活的一種記錄。 第一條:考慮用靜態工廠方法代替構造器 我們都是有身份證的人。 靜態工廠方法與構造器不同的最大優勢就在於它們都是有個人身份id的,雖然構造方法能夠通過改名引數型別或者引數個數等來區分,但一定意義上也會給開發帶來一定的問

effective Java 學習筆記

  第一條:考慮用靜態工廠方法代替建構函式 類可提供一個共有的靜態工廠方法,返回類的一個例項。 靜態工廠方法優點(與建構函式比較): 1、靜態工廠方法具有名字。 當有多個建構函式的時候,如果一個建構函式的引數不能明確的描述被返回物件的特徵,則選用適當名字的靜態方法可以更易閱

Java學習筆記

fun 編程語言 java 初始化 創建 abs 就是 p s 屬性 1.1. 一個數取反的算法 a取反,~a=-(a+1)。如 ~5=-6,~(-8)=7 1.2. Java中類的訪問控制符 (類的修飾符有public、default、abstract、final,方法的

JAVA學習筆記——

最簡 就業 計算 開發 目錄下的文件 -- 互聯 nvi 操作 今日內容介紹 1、Java開發環境搭建 2、HelloWorld案例 3、註釋、關鍵字、標識符 4、數據(數據類型、常量) 01java語言概述 * A: java語言概述 * a: Java是sun公

Thinking in Java學習筆記物件導論

  最近在看java程式設計思想,對其中自己的一些收穫記錄下來,一方面是加強鞏固自己的學習,另一方面也是方便以後翻閱檢視。 1、將物件看作服務提供者   把物件當作服務的提供者,也就是指關注物件能實現哪些功能,提供哪些服務?它需要哪些物件支援才能實現這些功能?   這樣做的好處就是:

java學習筆記int和Integer的區別

int和Integer的區別 1、Integer是int的包裝類,int則是java的一種基本資料型別 2、Integer變數必須例項化後才能使用,而int變數不需要 3、Integer實際是物件的引用,當new一個Integer時,實際上是生成一個指標指向此物件;而int則是直接儲存資料值

java學習筆記成員變數和區域性變數以及靜態變數的區別

成員變數和區域性變數的區別        成員變數:           ①成員變數定義在類中,在整個類中都可以被訪問。           ②成員

java學習筆記開發環境配置

我現在使用的windows平臺,主要介紹win平臺的安裝。                                  點選之後,進入下載頁面,                                    首先接受這個協議,然後下載自己對應的包。這

java學習筆記parseInt和valueOf 以及字串+和StringBuilder的區別

parseInt和valueOf 我們平時應該都用過或者見過parseInt和valueOf這兩個方法。一般我們是想把String型別的字元數字轉成int型別。從這個功能層面來說,這兩個方法都一樣,都可以勝任這個功能。 但是,我們進入原始碼,看下Integer類

java學習筆記 MVC模式

MVC模式 M 代表 模型(Model):應用程式中用於處理應用程式資料邏輯的部分,通常模型物件負責在資料庫中存取資料。 V 代表 檢視(View) :應用程式中處理資料顯示的部分。通常檢視是依據模型資料建立的。 C 代表 控制器(controller) :應用程式中處理使

Java學習筆記--常用的DOS命令 JDK的下載和安裝 配置path環境變數

常用的DOS命令 1.碟符切換 碟符:然後回車。 2.列出當前檔案及資料夾 dir 然後回車 3.建立目錄 md 目錄名稱 4.刪除目錄 rd 目錄名稱 5.進入指定目錄 -單集目錄

Effective Java 讀書筆記:建立和銷燬物件

1 構造器 => 靜態工廠方法 (1)優勢 靜態工廠方法有名字 靜態工廠方法不必在每次被呼叫時都產生一個新的物件 靜態工廠方法能返回原返回型別的任意子型別的物件 靜態工廠方法根據呼叫時傳入的不同引數而返回不同類的物件 靜態工廠方法返回物件的類不需要存在(SPI架構) (2

java學習筆記程式基本結構

Java程式設計的基本結構 1.一個簡單的Java應用程式(要十分注意java的大小寫) 關鍵字class表明Java的全部程式都在類中(這裡有一個對於類的有意思定義:將類看做是載入程式邏輯的容器,程式邏輯定義了應用程式的行為) *********************

Effective Java學習筆記對於所有物件都通用的方法

Object是一個具體類,但是設計他主要是為了擴充套件,他所有的非final方法(equals,toString,hashCode,clone,finalize)都是要被覆蓋的,並且任何一個類覆蓋非final方法時都要遵守通用原則,以便其他遵守這些預定的類能夠一同運作,

Effective Java 學習筆記

第八條改寫equals時總是要改寫hashCode 每個改寫了equals方法的類中,你必須也要改寫hashCode方法。 hashCode約定的內容: 1.在一個應用程式執行期間,如果一個物件的equals方法做比較所用到的資訊沒有被修改的話,則對該物件呼叫hashCode

effective java學習筆記

以下內容絕大部分來自《effective java》這本書,其中會有本人的一些學習是的想法,本文屬於偽原創 強烈建議大家在讀本文即後續文章時,先了解java的23中設計模式,可以看本人寫的關於設計模式的部落格或者 買本《大話設計模式》看看 第1條:考慮用靜態工廠方法代替構造

Java 8 流庫學習筆記

atm tlist see 條件 但是 表達 with ray 返回值 【core Java學習筆記】Java SE8 流庫 Stream Library 從叠代到流 如果要計算一個文本中有多少長單詞(字母>12)。 叠代式: words = getlist();/

JAVA程式設計思想學習筆記

物件導論 1.1 抽象過程 Smalltalk的五個基本特性: 萬物皆為物件。 程式是物件的集合,它通過傳送訊息來告知彼此所要做的。 每個物件都有自己的由其他物件所構成的儲存。 每個物件都有其型別。 某一特定型別的所有物件都可以接受同樣的訊息。

《自己動手寫java虛擬機器》學習筆記-----命令列工具go

     專案地址:https://github.com/gongxianshengjiadexiaohuihui 在今年三月份的時候,看過這本書,但是可能知識儲備不足,許多東西都一知半解,導致看到一半就看不下去了,現在覺得自己進步挺大的,決定重新拾起這本書,並且把

Java Web學習筆記

- Java Web介紹: Java Web,是用Java技術來解決相關web網際網路領域的技術總和。web包括:web伺服器和web客戶端兩部分。JavaWeb應用由一組Servlet、HTML頁、類、以及其它可以被繫結的資源構成。 JavaWeb應用中可以包含: - Servl