內(nèi)容摘引自《Effective Java》(第二版者吁,機械工業(yè)出版社)
第一條:考慮用靜態(tài)工廠方法代替構(gòu)造器
這樣做的好處
- 靜態(tài)工廠方法有名稱劈愚,能夠讓人更易閱讀浴滴,防止在開發(fā)過程中忘記應該使用哪個構(gòu)造器的情況
- 不必每次調(diào)用的時候都創(chuàng)建一個新的對象彻磁,當需要不可變類(實例內(nèi)容不可改變的類)的時候可以使用預先構(gòu)建好的實例,從而避免了重復創(chuàng)建對象蓝牲,提升了性能
- 它可以返回原返回類型的任何子類型對象趟脂,從而提高的靈活性
- 在創(chuàng)建參數(shù)化類型實例的時候泰讽,它能使得代碼變得簡潔
靜態(tài)工廠方法的缺點
- 類如果不含有公有的或者受保護的構(gòu)造器例衍,就不能被子類化
- 它與其他的靜態(tài)方法實際上并沒有任何區(qū)別,他可能不能像API文檔那樣直接明確標識出來
靜態(tài)方法的慣用名稱
- valueOf——類型轉(zhuǎn)換已卸,返回與它參數(shù)相同的值
- of——是valueOf的簡潔替代
- getInstance——返回的實例時通過方法的參數(shù)來描述的佛玄,但不能夠說與參數(shù)有相同的值。對于Singleton(單例)來說累澡,改方法沒有參數(shù)梦抢,并返回唯一的實例
- newInstance——類似于getInstance,但是newInstance能夠確保返回的每一個實例都與其他的不同
- getType——像getInstance一樣愧哟,表示工廠方法返回的數(shù)據(jù)類型奥吩,主要早不同的類的時候使用。
- newType——像newInstance一樣蕊梧,但是在工廠方法處于不同的類中的時候使用霞赫。Type表示工廠方法返回的對象類型
服務提供者框架
定于:多個服務提供者實現(xiàn)一個服務,系統(tǒng)為服務提供者的客戶端提供多個實現(xiàn)肥矢,并把他們從多個實現(xiàn)中解耦出來端衰。
這是一個靜態(tài)工廠方法的實例
服務提供者框架包含四個部分
- 服務接口(Service Interface),這是提供者實現(xiàn)的
- 提供者注冊API(Provider Registation API),這是系統(tǒng)用來注冊實現(xiàn)的旅东,讓客戶端訪問他們的灭抑。
- 服務訪問API(Service Access API),是客戶端用來獲取服務的實例的抵代。它一般允許但是不要求客戶端指定某種選擇提供者的條件腾节,如果客戶端沒有這樣的指定,那么一般會返回給一個默認的實例荤牍。它是一個“靈活的靜態(tài)工廠”禀倔,它構(gòu)成了服務提供者框架的基礎
- 服務提供者接口(Service Provider Interface),這是一個可選的参淫,這些提供者負責創(chuàng)建其服務實現(xiàn)的實例救湖。如果沒有提供實例那么實現(xiàn)就按照類名進行注冊,并通過反射方式進行實例化涎才。
第二條:遇到多個構(gòu)造器參數(shù)是要考慮用構(gòu)建器
當一個實體有多個屬性鞋既,而有的屬性是非必需的那么在創(chuàng)建實例的時候可能會有多個構(gòu)造函數(shù)可供選擇,舉例來說如下
package com.myclass;
public class OldPerson {
private int age;
private String name;
private double weight;
private String hobby;
public OldPerson(int age, String name, double weight, String hobby) {
// TODO Auto-generated constructor stub
this.age = age;
this.name = name;
this.weight = weight;
this.hobby = hobby;
}
//當weight和hobby不是必須的時候耍铜,我們在創(chuàng)建OldPerson就需要這樣
public OldPerson(int age, String name) {
this.age = age;
this.name = name;
}
}
如果有很多個參數(shù)那么構(gòu)造函數(shù)就可能會有很多邑闺,可能會在使用的時候不小心顛倒了兩個參數(shù)或者別的小問題,所以這種重疊構(gòu)造器模式是可以的棕兼,但是當有許多參數(shù)的時候陡舅,客戶端代碼會很難編寫,并且難以閱讀伴挚。
更好地方法
package com.myclass;
public class Person {
private int age;
private String name;
private double weight;
private String hobby;
public static class Builder {
//必填參數(shù)
private String name;
//選填參數(shù)靶衍,設置默認值
private int age = 0;
private double weight = 0.0;
private String hobby = "寫bug";
//設置必填參數(shù)
public Builder (String name) {
this.name = name;
}
//設置非必填參數(shù)
public Builder age(int age) {
this.age = age;
return this;
}
public Builder weight(int weight) {
this.weight = weight;
return this;
}
public Builder hobby(String hobby) {
this.hobby = hobby;
return this;
}
public Person build() {
return new Person(this);
}
}
public Person(Builder builder) {
// TODO Auto-generated constructor stub
age = builder.age;
name = builder.name;
weight = builder.weight;
hobby = builder.hobby;
}
}
這樣當我們使用的時候只需要這樣
Person person = new Person.Builder("Slience愛學習").weight(120).hobby("看書").build();
就可以了(雖然我用的比較多的是JavaBean模式)
第三條:用私有構(gòu)造器或者枚舉類型強化Singleton屬性
(這一條沒怎看懂)
第四條:通過私有構(gòu)造器強化不可實例化的能力
我們知道當類中沒有顯式的構(gòu)造器的時候,系統(tǒng)會自動生成一個缺省的構(gòu)造器茎芋,當我們想要讓一個類不能被實例的時候(比如說一些工具類)颅眶,我們可以自己寫一個構(gòu)造函數(shù),并把構(gòu)造函數(shù)設置為私有的田弥,這樣就不會創(chuàng)建這個實例了涛酗,舉例來說就是
package com.myclass;
public class MyClass {
public static String say() {
return "Hello World";
}
private MyClass() {
// TODO Auto-generated constructor stub
}
}
這樣我們就創(chuàng)建不了MyClass實例,而只能使用它的say方法了偷厦。
第五條:避免創(chuàng)建不必要的對象
String str = "Hello World";
String str2 = new String("Hello World");
str相對于str2效率要高商叹,因為在創(chuàng)建對象的時候"Hello World"已經(jīng)是一個對象了,沒有必要在用一層new String包裹起來再創(chuàng)建一個對象只泼。
再舉一個例子
long start = System.currentTimeMillis();
long sum = 0L;
for(long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);
System.out.println("耗時:" + (System.currentTimeMillis() - start));
和
long start = System.currentTimeMillis();
Long sum = 0L;
for(long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);
System.out.println("耗時:" + (System.currentTimeMillis() - start));
兩者的執(zhí)行耗時是不一樣的剖笙,后者使用Long的耗時更大,因為要將i轉(zhuǎn)成Long給sum加上辜妓,這意味著程序創(chuàng)造了不必要的很多個Long實例枯途,造成了更多的耗時忌怎。
第六條:消除過期的對象引用
(原書是一個棧增長再減少的例子),減少掉的元素可能不會被垃圾回收酪夷,在極端情況下可能會造成內(nèi)存泄漏榴啸。這是因為棧內(nèi)部維護者這些對象的過期應用。
過期應用是指永遠也不會被解除的引用晚岭。
解決這種問題的辦法也很簡單鸥印,那就是當對象引用過期的時候清空這些引用就可以了。
String[] strs = {"星期一","星期二","星期三","星期四"};
//如果要取出最后一個坦报,應該這樣
String str = strs[strs.length-1];
strs[strs.length-1] = null;
System.out.println(str);
這樣做的好處是當你不小心錯誤的解除引用库说,程序會報空指針異常而不是悄咪咪的運行下去。
第七條:避免使用終結(jié)方法
終結(jié)方法finalizer的線程優(yōu)先級比其他應用程序的線程要低片择,如果要使用finalizer去結(jié)束一個一個比較有限的資源潜的,比如說打開很多個文件的描述符(原文如此,可能是指explorer這樣的東東吧)字管,當你想要關掉它的時候再打開新的啰挪,因為優(yōu)先級低所以可能先去打開新的然后舊的沒有去關掉,關掉的速度比打開的速度低造成了資源的占用嘲叔。比較常見的終結(jié)方法有InputStream亡呵、java.sql.Connention中的colse方法。
顯示終結(jié)方法通常與try-finally結(jié)合起來使用硫戈,確保及時終止锰什。
終結(jié)方法前使用try-finally顯示終結(jié)的好處
- 可以充當終結(jié)方法的“安全網(wǎng)”,這樣就算終結(jié)方法沒有按時關閉也可以有所動作以便做一些補救丁逝。