国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

第6項:避免創建不需要的對象

Amio / 2087人閱讀

摘要:傳遞給構造器的參數本身就是一個實例,功能方面等同于構造器創建的所有對象。對于同時提供了靜態工廠方法第項和構造器的不可變類,通常可以使用靜態工廠方法而不是構造器,這樣可以經常避免創建不必要的對象。

??一般來說,最好能重用對象而不是在每次需要的時候就創建一個相同功能的新對象。重用的方式既快速,有流行。如果對象是不可變(immutable)的(第17項),那么就能重復使用它。

??作為一個極端的反面例子,考慮下面的語句:

String s = new String("bikini"); // DON"T DO THIS!

??該語句每次被執行的時候都創建一個新的String實例,但是這些對象的創建并不都是必要的。傳遞給String構造器的參數("bikini")本身就是一個String實例,功能方面等同于構造器創建的所有對象。如果這種方法是用在一個循環中,或者是在一個被頻繁調用的方法中,就會創建出成千上萬的不必要的String實例。

??改進后的版本如下:

String s = "bikini";

??這個版本只用了一個String實例,而不是每次執行時都創建一個新的實例。除此之外,它可以保證,對于所有在同一臺虛擬機中運行的代碼,只要它們包含相同的字符串字面常量,該對象就會被重用 [JLS, 3.10.5]。

??對于同時提供了靜態工廠方法(第1項)和構造器的不可變類,通常可以使用靜態工廠方法而不是構造器,這樣可以經常避免創建不必要的對象。例如,這個靜態工廠方法Boolean.valueOf(String)總是優先于在Java 9中拋棄的構造器 Boolean(String)。構造函數必須在每次調用時創建一個新對象,而工廠方法從不需要這樣做,也不會在實踐中。除了重用不可變對象之外,如果你知道它們不會被修改,你還可以重用可變對象。

??有些對象的創建比其他對象的代價大,如果你需要反復創建這種代價大的對象,建議將其緩存起來以便重復使用。不幸的是,當你創建這樣一個對象時,并不總是很明顯。假設你想編寫一個方法來確定一個字符串是否是一個有效的羅馬數字。使用正則表達式是最簡單的方法:

// Performance can be greatly improved!
static boolean isRomanNumeral(String s) {
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})" 
        + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}

??此實現的問題在于它依賴于String.matches方法。雖然String.matches是檢查字符串是否與正則表達式匹配的最簡單方法,但它不適合在性能關鍵的情況下重復使用。問題是它在內部為正則表達式創建了一個Pattern實例,并且只使用它一次,之后它就可能會被垃圾回收機制回收。創建Pattern實例的代價很大,因為它需要將正則表達式編譯為有限狀態機(because it requires compiling the regular expression into a finite state machine)。

??為了提高性能,將正則表達式顯式編譯為Pattern實例(不可變)作為類初始化的一部分,對其進行緩存,并在每次調用isRomanNumeral方法時重用相同的實例:

// Reusing expensive object for improved performance
public class RomanNumerals {
    private static final Pattern ROMAN = Pattern.compile(
        "^(?=.)M*(C[MD]|D?C{0,3})"
        + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
    static boolean isRomanNumeral(String s) {
        return ROMAN.matcher(s).matches();
    }
}

??如果經常調用的話,改進版本的isRomanNumeral可以顯著提高性能。在我的機器上,原始版本在8個字符的輸入字符串上需要1.1μs,而改進版本需要0.17μs,這是6.5倍的速度。不僅提高了性能,而且功能更加明了。為不可見的Pattern實例創建一個靜態的final字段,我們可以給它一個名字,它比正則表達式本身更具有可讀性。

??如果初始化包含改進版本的isRimanNumberal方法的類時,但是從不調用該方法,則不需要初始化字段ROMAN。在第一次調用isRimanNumberal方法時,可以通過延遲初始化字段(第83項)來消除使用時未初始化的影響,但不建議這樣做。延遲初始化的做法通常都有一個情況,那就是它會把實現復雜化,從而導致無法測試它的性能改進情況。

??當一個對象是不可變的,那么就可以安全地重用它,但是在其他情況下,它并不是那么明顯,甚至違反直覺。這時候可以考慮使用適配器 [Gamma95],也稱為視圖。適配器是委托給支持對象的對象(An adapter is an object that delegates to a backing object),它提供一個備用接口。因為適配器的狀態不超過其支持對象的狀態,所以不需要為給定對象創建一個給定適配器的多個實例。

??例如,Map接口的keySet方法返回Map對象的Set視圖,該視圖由Map中的所有鍵組成。看起來,似乎每次調用keySet都必須創建一個新的Set實例,但是對給定Map對象上的keySet的每次調用都可能返回相同的Set實例。盡管返回的Set實例通常是可變的,但所有返回的對象在功能上都是相同的:當其中一個返回的對象發生更改時,所有其他對象也會發生更改,因為它們都由相同的Map實例支持。雖然創建keySet視圖對象的多個實例在很大程度上是無害的,但不必要這樣做,并且這樣做沒有任何好處。

??創建不必要的對象的另一種方式是自動裝箱,它允許程序猿將基本類型和裝箱基本類型(Boxed Primitive Type)混用,按需自動裝箱和拆箱。自動裝箱使得基本類型和裝箱基本類型之間的差別變得模糊起來,但是并沒有完全消除。它們在語義上還有微妙的差別,在性能上也有著比較明顯的差別(第61項)。請考慮以下方法,該方法計算所有正整數值的總和,為此,程序必須使用long類型,因為int類型無法容納所有正整數的總和:

// Hideously slow! Can you spot the object creation?
private static long sum() {
    Long sum = 0L;
    for (long i = 0; i <= Integer.MAX_VALUE; i++)
        sum += i;
    return sum;
}

??這段程序算出的答案是正確的,但是比實際情況要更慢一些,只因為錯打了一個字符。變量sum被聲明成Long而不是long,意味著程序構造了大約 2^31 個多余的Long實例(大約每次往Long sum中增加long時構造一個實例)。將sum的聲明從Long改成long,在我的機器上運行時間從43秒減少到了6秒。結論很明顯:要優先使用基本類型而不是裝箱基本類型,要當心無意識的自動裝箱。

??不要錯誤地認為本項所介紹的內容暗示著“創建對象的代價非常昂貴,我們就應該盡可能地避免創建對象”。相反,由于小對象的構造器只做很少量的顯示工作,所以,小對象的創建和回收動作是非常廉價的,特別是在現代的JVM實現上更是如此。通過創建附加的對象,提升程序的清晰性、簡潔性和功能性,這通常是件好事。

??反之,通過維護自己的對象池(object pool)來避免創建對象并不是一種好的做法,除非池中的對象是非常重量級的。真正正確使用對象池的經典對象示例就是數據庫連接池。建立數據庫連接的代價是非常昂貴的,因此重用這些對象非常有意義。但是,通常來說,維護自己的對象池必定會把代碼弄得很亂,同時增加內存占用,而且會影響性能。現代的JVM實現具有高度優化的垃圾回收器,其性能很容易就會超過輕量級對象池的性能。

??與本項對應的是第50項的“保護性拷貝”的內容。該項說得是:你應該重用已經存在的對象,而不是去創建一個新的對象。然而第50項說的是:你應該創建一個新的對象而不是重用一個已經存在的對象。注意,在提倡使用保護性拷貝的時候,因重用對象而付出的代價要遠遠大于因創建重復對象而付出的代價。必要時如果沒能實施保護性拷貝,將會導致潛在的錯誤和安全漏洞,而不必要地創建對象則只會影響程序的風格和性能。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77469.html

相關文章

  • Effective Java 三版 全文翻譯

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

    galois 評論0 收藏0
  • Effective Java 3rd.Edition 翻譯

    摘要:推薦序前言致謝第一章引言第二章創建和銷毀對象第項用靜態工廠方法代替構造器第項遇到多個構造器參數時要考慮使用構建器第項用私有構造器或者枚舉類型強化屬性第項通過私有構造器強化不可實例化的能力第項優先考慮依賴注入來引用資源第項避免創建不必要的對象 推薦序 前言 致謝 第一章 引言 第二章 創建和銷毀對象 第1項:用靜態工廠方法代替構造器 第2項:遇到多個構造器參數時要考慮使用構建器 第...

    KoreyLee 評論0 收藏0
  • 二章 創建和銷毀對象

    摘要:一個類可以提供一個公共靜態工廠方法,它僅僅是一第項遇到多個構造器參數時要考慮使用構建器靜態工廠和構造器有個共同的局限性他們都不能很好地擴展到大量的可選參數。 ??本章涉及創建和銷毀對象,包括何時以及如何創建它們,何時以及如何避免創建它們,如何確保它們被及時銷毀,以及如何管理在銷毀之前必須進行的清理操作。 第1項:用靜態工廠方法代替構造器 ??類允許客戶端獲取實例的傳統方法是提供公共構造...

    Jeffrrey 評論0 收藏0
  • 1:考慮靜態工廠方法而是構造函數

    摘要:提供靜態工廠方法而不是公共構造函數既有優點也有缺點。它們不像構造函數那樣在文檔中脫穎而出,因此很難弄清楚如何實例化提供靜態工廠方法而不是構造函數的類。 ??類允許客戶端獲取實例的傳統方法是提供公共構造器。還有一種技術應該是每個程序員的工具箱的一部分。一個類可以提供一個公共靜態工廠方法,它僅僅是一個返回類實例的靜態方法。下面是布爾(布爾型的盒裝原語類)的一個簡單示例。這個方法將一個布爾原...

    趙連江 評論0 收藏0

發表評論

0條評論

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