《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