成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【Effective Java】創(chuàng)建和銷毀對象

Hwg / 2516人閱讀

摘要:一考慮用靜態(tài)工廠方法代替構(gòu)造器構(gòu)造器是創(chuàng)建一個對象實(shí)例的最基本最常用的方法。開發(fā)者在使用某個類的時候,通常會使用一個構(gòu)造器來實(shí)現(xiàn),其實(shí)也有其他方式可以實(shí)現(xiàn)的,如利用發(fā)射機(jī)制。

一、考慮用靜態(tài)工廠方法代替構(gòu)造器

構(gòu)造器是創(chuàng)建一個對象實(shí)例的最基本最常用的方法。開發(fā)者在使用某個類的時候,通常會使用new一個構(gòu)造器來實(shí)現(xiàn),其實(shí)也有其他方式可以實(shí)現(xiàn)的,如利用發(fā)射機(jī)制。這里主要說的是通過靜態(tài)類工廠的方式來創(chuàng)建class的實(shí)例,如:

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

靜態(tài)工廠方法和構(gòu)造器不同有以下主要優(yōu)勢:

1. 有意義的名稱

可能有多個構(gòu)造器,不同構(gòu)造器有不同的參數(shù),而參數(shù)本身并不能確切地描述被返回的對象,所以顯得有點(diǎn)模糊,而具有適當(dāng)名稱的靜態(tài)工廠可讀性更強(qiáng),表達(dá)也更清晰。

如,構(gòu)造器BigInteger(int, int, Random)返回一個BigInteger可能是一個素數(shù),改名為BigInteger.probablePrime的靜態(tài)工廠方法表示也就更加清晰。

2. 不必在每次調(diào)用的時候創(chuàng)建一個新的對象

這樣可以避免創(chuàng)建不必要的重復(fù)對象,提高程序效率。

3. 可以返回原返回類型的任何子類型的對象

Java的很多服務(wù)提供者框架(ServiceProvider Framework,三個主要組件:服務(wù)接口(Service Interface)這是提供者實(shí)現(xiàn)的;提供者注冊API(Provider Registration API),這是系統(tǒng)用來注冊實(shí)現(xiàn),讓客戶端訪問它的;服務(wù)訪問API(Service Access API),是客戶端用來獲取服務(wù)實(shí)例的??蛇x組件:服務(wù)提供者接口(Service Provider Interface),提供者負(fù)責(zé)創(chuàng)建其服務(wù)實(shí)現(xiàn)的實(shí)例)都運(yùn)用到這個特性,如JDBC的API。

下面是一個包含一個服務(wù)提供者接口和一個默認(rèn)提供者:

//Service interface
public interface Service{
    //...service methods
}
//Service provider interface
public interface Provider{
    Service newService();
}
//noninstantiable class for service registration and access
public class Service{
    private Service(){}

    //Maps service names to services
    private static final Map providers=new ConcurrentHashMap();
    public static final String DEFAULT_PROVIDER_NAME="";
    //Provider registration API
    public static void registerDefaultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME);
    }
    public static void registerProvider(String name,Provider p){
        providers.put(name, p);
    }
    //Service access API
    public static Service newInstance(){
        return newInstace(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();
    }
}
4. 在創(chuàng)建參數(shù)化類型實(shí)例的時候,它們使代碼變得更加簡潔

原來:

Map> map=new HashMap>();

改為靜態(tài)工廠方法,可以利用參數(shù)類型推演的優(yōu)勢,避免了類型參數(shù)在一次聲明中被多次重寫所帶來的煩憂:

public static  HashMap newInstance() {
    return new HashMap();
}
Map m = MyHashMap.newInstance();

當(dāng)然,靜態(tài)方法也存在缺點(diǎn):

類如果不含公有的或者受保護(hù)的構(gòu)造器,就不能被子類化;

與其他的靜態(tài)方法實(shí)際上沒有任何區(qū)別(API沒有像構(gòu)造器那樣標(biāo)識出來)

不過,對于靜態(tài)工廠方法和構(gòu)造器,通常優(yōu)先考慮靜態(tài)工廠方法。

二、遇到多個構(gòu)造參數(shù)時要考慮用構(gòu)建器(Builder模式)

靜態(tài)工廠和構(gòu)造器有一個共同的局限性:它們都不能很好地擴(kuò)展到大量的可選參數(shù)。當(dāng)然可以通過以下方法解決:

方法一:利用重疊構(gòu)造器模式(就是需要多少個參數(shù)就在參數(shù)列表添加多少個),但是當(dāng)有很多個參數(shù)時,客戶端代碼會很難編寫,并且難以閱讀;

方法二:JavaBeans模式,調(diào)用一個無參構(gòu)造函數(shù),然后調(diào)用setter方法來設(shè)置每個必要的參數(shù),但調(diào)用的過程中可能會出現(xiàn)不一致的狀態(tài),調(diào)試比較麻煩;

方法三:Builder模式。不直接生成想要的對象,而是讓客戶端利用所有必要的參數(shù)調(diào)用構(gòu)造器(或靜態(tài)方法),得到一個builder對象,然后再在builder對象對每個參數(shù)對應(yīng)的方法進(jìn)行調(diào)用來設(shè)置,如下:

class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;
    public static class Builder {
        //對象的必選參數(shù)
        private final int servingSize;
        private final int servings;
        //對象的可選參數(shù)的缺省值初始化
        private int calories = 0;
        private int fat = 0;
        private int carbohydrate = 0;
        private int sodium = 0;
        //只用少數(shù)的必選參數(shù)作為構(gòu)造器的函數(shù)參數(shù)
        public Builder(int servingSize,int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }
        public Builder calories(int val) {
            calories = val;
            return this;
        }
        public Builder fat(int val) {
            fat = val;
            return this;
        }
        public Builder carbohydrate(int val) {
            carbohydrate = val;
            return this;
        }
        public Builder sodium(int val) {
            sodium = val;
            return this;
        }
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }
    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}
//使用方式
public static void main(String[] args) {
    NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100)
        .sodium(35).carbohydrate(27).build();
    System.out.println(cocaCola);
}

所以,如果類的構(gòu)造器或者靜態(tài)工廠中具有多個參數(shù),設(shè)計這種類時,Builder模式就是種不錯的選擇!

三、用私有構(gòu)造器或者枚舉類型強(qiáng)化Singleton屬性

Singleton模式經(jīng)常會被用到,它被用來代表那些本質(zhì)上唯一的系統(tǒng)組件,如窗口管理器或者文件系統(tǒng)。在Java中實(shí)現(xiàn)單例模式主要有三種:

將構(gòu)造函數(shù)私有化,直接通過靜態(tài)公有的final域字段獲取單實(shí)例對象:

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elivs() { ... }
    public void leaveTheBuilding() { ... }
}

這樣的方式主要優(yōu)勢在于簡潔高效,使用者很快就能判定當(dāng)前類為單實(shí)例類,在調(diào)用時直接操作Elivs.INSTANCE即可,由于沒有函數(shù)的調(diào)用,因此效率也非常高效。然而事物是具有一定的雙面性的,這種設(shè)計方式在一個方向上走的過于極端了,因此他的缺點(diǎn)也會是非常明顯的。如果今后Elvis的使用代碼被遷移到多線程的應(yīng)用環(huán)境下了,系統(tǒng)希望能夠做到每個線程使用同一個Elvis實(shí)例,不同線程之間則使用不同的對象實(shí)例。那么這種創(chuàng)建方式將無法實(shí)現(xiàn)該需求,因此需要修改接口以及接口的調(diào)用者代碼,這樣就帶來了更高的修改成本。

通過公有域成員的方式返回單實(shí)例對象:

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elivs() { ... }
    public static Elvis getInstance() { return INSTANCE; }
    public void leaveTheBuilding() { ... }
}

這種方法很好的彌補(bǔ)了第一種方式的缺陷,如果今后需要適應(yīng)多線程環(huán)境的對象創(chuàng)建邏輯,僅需要修改Elvis的getInstance()方法內(nèi)部即可,對用調(diào)用者而言則是不變的,這樣便極大的縮小了影響的范圍。至于效率問題,現(xiàn)今的JVM針對該種函數(shù)都做了很好的內(nèi)聯(lián)優(yōu)化,因此不會產(chǎn)生因函數(shù)頻繁調(diào)用而帶來的開銷。

使用枚舉的方式(Java SE5):

public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() { ... }
}

就目前而言,這種方法在功能上和公有域方式相近,但是他更加簡潔更加清晰,擴(kuò)展性更強(qiáng)也更加安全。雖然這種方法還沒被廣泛采用,但單元素的枚舉類型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法。

四、通過私有構(gòu)造器強(qiáng)化不可實(shí)例化的能力

對于有些工具類如java.lang.Math、java.util.Arrays等,其中只是包含了靜態(tài)方法和靜態(tài)域字段,因此對這樣的class實(shí)例化就顯得沒有任何意義了。然而在實(shí)際的使用中,如果不加任何特殊的處理,這樣的classes是可以像其他classes一樣被實(shí)例化的。這里介紹了一種方式,既將缺省構(gòu)造函數(shù)設(shè)置為private,這樣類的外部將無法實(shí)例化該類,與此同時,在這個私有的構(gòu)造函數(shù)的實(shí)現(xiàn)中直接拋出異常,從而也避免了類的內(nèi)部方法調(diào)用該構(gòu)造函數(shù)。

public class UtilityClass {
    //Suppress default constructor for noninstantiability.
    private UtilityClass() {
        throw new AssertionError();
    }
}

這樣定義之后,該類將不會再被外部實(shí)例化了,否則會產(chǎn)生編譯錯誤。然而這樣的定義帶來的最直接的負(fù)面影響是該類將不能再被子類化。

五、避免創(chuàng)建不必要的對象

一般來說,最好能重用對象而不是在每次需要的時候創(chuàng)建一個相同功能的新對象。

試比較以下兩行代碼在被多次反復(fù)執(zhí)行時的效率差異:

    String s = new String("stringette"); //don"t do this
    String s = "stringette";

由于String被實(shí)現(xiàn)為不可變對象,JVM底層將其實(shí)現(xiàn)為常量池,既所有值等于"stringette" 的String對象實(shí)例共享同一對象地址,而且還可以保證,對于所有在同一JVM中運(yùn)行的代碼,只要他們包含相同的字符串字面常量,該對象就會被重用。

我們繼續(xù)比較下面的例子,并測試他們在運(yùn)行時的效率差異:

    Boolean b = Boolean.valueOf("true");
    Boolean b = new Boolean("true");

前者通過靜態(tài)工廠方法保證了每次返回的對象,如果他們都是true或false,那么他們將返回相同的對象。換句話說,valueOf將只會返回Boolean.TRUE或Boolean.FALSE兩個靜態(tài)域字段之一。而后面的Boolean構(gòu)造方式,每次都會構(gòu)造出一個新的Boolean實(shí)例對象。這樣在多次調(diào)用后,第一種靜態(tài)工廠方法將會避免大量不必要的Boolean對象被創(chuàng)建,從而提高了程序的運(yùn)行效率,也降低了垃圾回收的負(fù)擔(dān)。

繼續(xù)比較下面的代碼:

public class Person {
    private final Date birthDate;
    //判斷該嬰兒是否是在生育高峰期出生的。
    public boolean isBabyBoomer {
        Calender c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        c.set(1946,Calendar.JANUARY,1,0,0,0);
        Date dstart = c.getTime();
        c.set(1965,Calendar.JANUARY,1,0,0,0);
        Date dend = c.getTime();
        return birthDate.compareTo(dstart) >= 0 && birthDate.compareTo(dend) < 0;
    }
}
//修改后
public class Person {
    private static final Date BOOM_START;
    private static final Date BOOM_END;

    static {
        Calender c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        c.set(1946,Calendar.JANUARY,1,0,0,0);
        BOOM_START = c.getTime();
        c.set(1965,Calendar.JANUARY,1,0,0,0);
        BOOM_END = c.getTime();
    }
    public boolean isBabyBoomer() {
        return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0;
    }
}

改進(jìn)后的Person類只是在初始化的時候創(chuàng)建Calender、TimeZone和Date實(shí)例一次,而不是在每次調(diào)用isBabyBoomer方法時都創(chuàng)建一次他們。如果該方法會被頻繁調(diào)用,效率的提升將會極為顯著。

集合框架中的Map接口提供keySet方法,該方法每次都將返回底層原始Map對象鍵數(shù)據(jù)的視圖,而并不會為該操作創(chuàng)建一個Set對象并填充底層Map所有鍵的對象拷貝。因此當(dāng)多次調(diào)用該方法并返回不同的Set對象實(shí)例時,事實(shí)上他們底層指向的將是同一段數(shù)據(jù)的引用。

在該條目中還提到了自動裝箱行為給程序運(yùn)行帶來的性能沖擊,如果可以通過原始類型完成的操作應(yīng)該盡量避免使用裝箱類型以及他們之間的交互使用。見下例:

public static void main(String[] args) {
    Long sum = 0L;  //注意Long與long
    for (long i = 0; i < Integer.MAX_VALUE; ++i) {
        sum += i;
    }
    System.out.println(sum);
}

本例中由于錯把long sum定義成Long sum,其效率降低了近10倍,這其中的主要原因便是該錯誤導(dǎo)致了2的31次方個臨時Long對象被創(chuàng)建了。要優(yōu)先使用基本類型而不是裝箱基本類型,要當(dāng)心無意識的自動裝箱。

六、消除過期的對象引用

盡管Java的JVM垃圾回收機(jī)制對內(nèi)存進(jìn)行智能管理了,不像C++那樣需要手動管理,但只是因為如此,Java中內(nèi)存泄露變得更加隱匿,更加難以發(fā)現(xiàn),見如下代碼:

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }
    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }
    public Object pop() {
        if (size == 0) 
            throw new EmptyStackException();
        return elements[--size];
    }
    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copys(elements,2*size+1);
    }
}

以上示例代碼,在正常的使用中不會產(chǎn)生任何邏輯問題,然而隨著程序運(yùn)行時間不斷加長,內(nèi)存泄露造成的副作用將會慢慢的顯現(xiàn)出來,如磁盤頁交換、OutOfMemoryError等。那么內(nèi)存泄露隱藏在程序中的什么地方呢?當(dāng)我們調(diào)用pop方法是,該方法將返回當(dāng)前棧頂?shù)膃lements,同時將該棧的活動區(qū)間(size)減一,然而此時被彈出的Object仍然保持至少兩處引用,一個是返回的對象,另一個則是該返回對象在elements數(shù)組中原有棧頂位置的引用。這樣即便外部對象在使用之后不再引用該Object,那么它仍然不會被垃圾收集器釋放,久而久之導(dǎo)致了更多類似對象的內(nèi)存泄露。修改方式如下:

public Object pop() {
    if (size == 0) 
        throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null; //手工將數(shù)組中的該對象置空
    return result;
}

由于現(xiàn)有的Java垃圾收集器已經(jīng)足夠只能和強(qiáng)大,因此沒有必要對所有不在需要的對象執(zhí)行obj = null的顯示置空操作,這樣反而會給程序代碼的閱讀帶來不必要的麻煩,該條目只是推薦在以下3中情形下需要考慮資源手工處理問題:

類是自己管理內(nèi)存,如例子中的Stack類。

使用對象緩存機(jī)制時,需要考慮被從緩存中換出的對象,或是長期不會被訪問到的對象。

事件監(jiān)聽器和相關(guān)回調(diào)。用戶經(jīng)常會在需要時顯示的注冊,然而卻經(jīng)常會忘記在不用的時候注銷這些回調(diào)接口實(shí)現(xiàn)類。

七、避免使用終結(jié)方法

終結(jié)方法(finalizer)通常是不可預(yù)測的,也是很危險的,一般情況下是不必要的。使用終結(jié)方法會導(dǎo)致行為不穩(wěn)定、降低性能,以及可移植性問題。

在Java中完成這樣的工作主要是依靠try-finally機(jī)制來協(xié)助完成的。然而Java中還提供了另外一種被稱為finalizer的機(jī)制,使用者僅僅需要重載Object對象提供的finalize方法,這樣當(dāng)JVM的在進(jìn)行垃圾回收時,就可以自動調(diào)用該方法。但是由于對象何時被垃圾收集的不確定性,以及finalizer給GC帶來的性能上的影響,因此并不推薦使用者依靠該方法來達(dá)到關(guān)鍵資源釋放的目的。比如,有數(shù)千個圖形句柄都在等待被終結(jié)和回收,可惜的是執(zhí)行終結(jié)方法的線程優(yōu)先級要低于普通的工作者線程,這樣就會有大量的圖形句柄資源停留在finalizer的隊列中而不能被及時的釋放,最終導(dǎo)致了系統(tǒng)運(yùn)行效率的下降,甚至還會引發(fā)JVM報出OutOfMemoryError的錯誤。

Java的語言規(guī)范中并沒有保證該方法會被及時的執(zhí)行,甚至都沒有保證一定會被執(zhí)行。即便開發(fā)者在code中手工調(diào)用了 System.gcSystem.runFinalization 這兩個方法,這僅僅是提高了finalizer被執(zhí)行的幾率而已。還有一點(diǎn)需要注意的是,被重載的finalize()方法中如果拋出異常,其棧幀軌跡是不會被打印出來的。在Java中被推薦的資源釋放方法為,提供顯式的具有良好命名的接口方法,如 FileInputStream.close()Graphic2D.dispose() 等。然后使用者在finally區(qū)塊中調(diào)用該方法,見如下代碼:

public void test() {
    FileInputStream fin = null;
    try {
        fin = new FileInputStream(filename);
        //do something.
    } finally {
        fin.close();
    }
}

在實(shí)際的開發(fā)中,利用finalizer又能給我們帶來什么樣的幫助呢?見下例:

public class FinalizeTest {
    //@Override
    protected void finalize() throws Throwable {
        try {
            //在調(diào)試過程中通過該方法,打印對象在被收集前的各種狀態(tài),
            //如判斷是否仍有資源未被釋放,或者是否有狀態(tài)不一致的現(xiàn)象存在。
            //推薦將該finalize方法設(shè)計成僅在debug狀態(tài)下可用,而在release
            //下該方法并不存在,以避免其對運(yùn)行時效率的影響。
            System.out.println("The current status: " + _myStatus);
        } finally {
            //在finally中對超類finalize方法的調(diào)用是必須的,這樣可以保證整個class繼承
            //體系中的finalize鏈都被執(zhí)行。
            super.finalize(); 
        }
    }
}
  

整理參考自《Effective Java》和 Effective Java (創(chuàng)建和銷毀對象)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/64244.html

相關(guān)文章

  • Effective Java》學(xué)習(xí)筆記 第二章 創(chuàng)建銷毀對象

    摘要:第二章創(chuàng)建和銷毀對象何時以及如何創(chuàng)建對象,何時以及如何避免創(chuàng)建對象,如何確保他們能夠適時地銷毀,以及如何管理對象銷毀之前必須進(jìn)行的各種清理動作。表示工廠方法所返回的對象類型。 第二章 創(chuàng)建和銷毀對象 何時以及如何創(chuàng)建對象,何時以及如何避免創(chuàng)建對象,如何確保他們能夠適時地銷毀,以及如何管理對象銷毀之前必須進(jìn)行的各種清理動作。 1 考慮用靜態(tài)工廠方法代替構(gòu)造器 一般在某處獲取一個類的實(shí)例最...

    tinylcy 評論0 收藏0
  • Effective Java 第三版 全文翻譯

    摘要:本章中的大部分內(nèi)容適用于構(gòu)造函數(shù)和方法。第項其他方法優(yōu)先于序列化第項謹(jǐn)慎地實(shí)現(xiàn)接口第項考慮使用自定義的序列化形式第項保護(hù)性地編寫方法第項對于實(shí)例控制,枚舉類型優(yōu)先于第項考慮用序列化代理代替序列化實(shí)例附錄與第版中項目的對應(yīng)關(guān)系參考文獻(xiàn) effective-java-third-edition 介紹 Effective Java 第三版全文翻譯,純屬個人業(yè)余翻譯,不合理的地方,望指正,感激...

    galois 評論0 收藏0
  • Effective Java筆記

    摘要:構(gòu)造器的參數(shù)沒有確切地描述其返回的對象,適當(dāng)名稱的靜態(tài)工廠方法更容易使用,也易于閱讀。在文檔中,沒有像構(gòu)造器那樣明確標(biāo)識出來,因此,對于提供了靜態(tài)工廠方法而不是構(gòu)造器的類來說,要查明如何實(shí)例化一個類,有點(diǎn)困難。 第二章 創(chuàng)建和銷毀對象 第1條 考慮用靜態(tài)工廠方法代替構(gòu)造器 兩者創(chuàng)建對象的形式,例如:構(gòu)造器是new Boolean();靜態(tài)工廠方法是 public static Bool...

    Drummor 評論0 收藏0
  • Effective Java 之個人總結(jié)

    摘要:模式下使用來設(shè)置各個參數(shù),無法僅通過檢驗構(gòu)造器參數(shù)的有效性來保證一致性,會試圖使用不一致狀態(tài)的對象。消除過期的對象引用緩存時優(yōu)先使用,這些數(shù)據(jù)結(jié)構(gòu),及時清掉沒用的項。顯示取消監(jiān)聽器和回調(diào),或進(jìn)行弱引用。 創(chuàng)建和銷毀對象 1、靜態(tài)工廠方法代替構(gòu)造器 靜態(tài)工廠方法有名稱,能確切地描述正被返回的對象。 不必每次調(diào)用都創(chuàng)建一個新的對象。 可以返回原返回類型的任何子類對象。 創(chuàng)建參數(shù)化類型實(shí)例...

    maxmin 評論0 收藏0

發(fā)表評論

0條評論

Hwg

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<